// Copyright 2017 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package openstack import ( "fmt" "net" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" "github.com/gophercloud/gophercloud/pagination" "github.com/prometheus/common/log" "github.com/prometheus/common/model" "golang.org/x/net/context" "github.com/prometheus/prometheus/config" ) const ( openstackLabelHypervisorHostIP = openstackLabelPrefix + "hypervisor_host_ip" openstackLabelHypervisorHostName = openstackLabelPrefix + "hypervisor_hostname" openstackLabelHypervisorStatus = openstackLabelPrefix + "hypervisor_status" openstackLabelHypervisorState = openstackLabelPrefix + "hypervisor_state" openstackLabelHypervisorType = openstackLabelPrefix + "hypervisor_type" ) // HypervisorDiscovery discovers OpenStack hypervisors. type HypervisorDiscovery struct { authOpts *gophercloud.AuthOptions region string interval time.Duration logger log.Logger port int } // NewHypervisorDiscovery returns a new hypervisor discovery. func NewHypervisorDiscovery(opts *gophercloud.AuthOptions, interval time.Duration, port int, region string, l log.Logger) *HypervisorDiscovery { return &HypervisorDiscovery{authOpts: opts, region: region, interval: interval, port: port, logger: l} } // Run implements the TargetProvider interface. func (h *HypervisorDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) { // Get an initial set right away. tg, err := h.refresh() if err != nil { h.logger.Error(err) } else { select { case ch <- []*config.TargetGroup{tg}: case <-ctx.Done(): return } } ticker := time.NewTicker(h.interval) defer ticker.Stop() for { select { case <-ticker.C: tg, err := h.refresh() if err != nil { h.logger.Error(err) continue } select { case ch <- []*config.TargetGroup{tg}: case <-ctx.Done(): return } case <-ctx.Done(): return } } } func (h *HypervisorDiscovery) refresh() (*config.TargetGroup, error) { var err error t0 := time.Now() defer func() { refreshDuration.Observe(time.Since(t0).Seconds()) if err != nil { refreshFailuresCount.Inc() } }() provider, err := openstack.AuthenticatedClient(*h.authOpts) if err != nil { return nil, fmt.Errorf("could not create OpenStack session: %s", err) } client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ Region: h.region, }) if err != nil { return nil, fmt.Errorf("could not create OpenStack compute session: %s", err) } tg := &config.TargetGroup{ Source: fmt.Sprintf("OS_" + h.region), } // OpenStack API reference // https://developer.openstack.org/api-ref/compute/#list-hypervisors-details pagerHypervisors := hypervisors.List(client) err = pagerHypervisors.EachPage(func(page pagination.Page) (bool, error) { hypervisorList, err := hypervisors.ExtractHypervisors(page) if err != nil { return false, fmt.Errorf("could not extract hypervisors: %s", err) } for _, hypervisor := range hypervisorList { labels := model.LabelSet{ openstackLabelHypervisorHostIP: model.LabelValue(hypervisor.HostIP), } addr := net.JoinHostPort(hypervisor.HostIP, fmt.Sprintf("%d", h.port)) labels[model.AddressLabel] = model.LabelValue(addr) labels[openstackLabelHypervisorHostName] = model.LabelValue(hypervisor.HypervisorHostname) labels[openstackLabelHypervisorHostIP] = model.LabelValue(hypervisor.HostIP) labels[openstackLabelHypervisorStatus] = model.LabelValue(hypervisor.Status) labels[openstackLabelHypervisorState] = model.LabelValue(hypervisor.State) labels[openstackLabelHypervisorType] = model.LabelValue(hypervisor.HypervisorType) tg.Targets = append(tg.Targets, labels) } return true, nil }) if err != nil { return nil, err } return tg, nil }