discovery/openstack: discover all interfaces (#4649)

* discovery/openstack: discover all interfaces
* Add address pool label

Signed-off-by: Simon Pasquier <spasquie@redhat.com>
This commit is contained in:
Simon Pasquier 2018-10-09 17:17:08 +02:00 committed by Brian Brazil
parent 33c97d73e4
commit a2a78d0a09
4 changed files with 140 additions and 60 deletions

View File

@ -34,6 +34,7 @@ import (
const (
openstackLabelPrefix = model.MetaLabelPrefix + "openstack_"
openstackLabelAddressPool = openstackLabelPrefix + "address_pool"
openstackLabelInstanceID = openstackLabelPrefix + "instance_id"
openstackLabelInstanceName = openstackLabelPrefix + "instance_name"
openstackLabelInstanceStatus = openstackLabelPrefix + "instance_status"
@ -100,6 +101,11 @@ func (i *InstanceDiscovery) Run(ctx context.Context, ch chan<- []*targetgroup.Gr
}
}
type floatingIPKey struct {
id string
fixed string
}
func (i *InstanceDiscovery) refresh() (*targetgroup.Group, error) {
var err error
t0 := time.Now()
@ -124,7 +130,8 @@ func (i *InstanceDiscovery) refresh() (*targetgroup.Group, error) {
// OpenStack API reference
// https://developer.openstack.org/api-ref/compute/#list-floating-ips
pagerFIP := floatingips.List(client)
floatingIPList := make(map[string][]string)
floatingIPList := make(map[floatingIPKey]string)
floatingIPPresent := make(map[string]struct{})
err = pagerFIP.EachPage(func(page pagination.Page) (bool, error) {
result, err := floatingips.ExtractFloatingIPs(page)
if err != nil {
@ -132,9 +139,11 @@ func (i *InstanceDiscovery) refresh() (*targetgroup.Group, error) {
}
for _, ip := range result {
// Skip not associated ips
if ip.InstanceID != "" {
floatingIPList[ip.InstanceID] = append(floatingIPList[ip.InstanceID], ip.IP)
if ip.InstanceID == "" || ip.FixedIP == "" {
continue
}
floatingIPList[floatingIPKey{id: ip.InstanceID, fixed: ip.FixedIP}] = ip.IP
floatingIPPresent[ip.IP] = struct{}{}
}
return true, nil
})
@ -156,14 +165,28 @@ func (i *InstanceDiscovery) refresh() (*targetgroup.Group, error) {
}
for _, s := range instanceList {
labels := model.LabelSet{
openstackLabelInstanceID: model.LabelValue(s.ID),
}
if len(s.Addresses) == 0 {
level.Info(i.logger).Log("msg", "Got no IP address", "instance", s.ID)
continue
}
for _, address := range s.Addresses {
labels := model.LabelSet{
openstackLabelInstanceID: model.LabelValue(s.ID),
openstackLabelInstanceStatus: model.LabelValue(s.Status),
openstackLabelInstanceName: model.LabelValue(s.Name),
}
id, ok := s.Flavor["id"].(string)
if !ok {
level.Warn(i.logger).Log("msg", "Invalid type for flavor id, expected string")
continue
}
labels[openstackLabelInstanceFlavor] = model.LabelValue(id)
for k, v := range s.Metadata {
name := strutil.SanitizeLabelName(k)
labels[openstackLabelTagPrefix+model.LabelName(name)] = model.LabelValue(v)
}
for pool, address := range s.Addresses {
md, ok := address.([]interface{})
if !ok {
level.Warn(i.logger).Log("msg", "Invalid type for address, expected array")
@ -173,38 +196,35 @@ func (i *InstanceDiscovery) refresh() (*targetgroup.Group, error) {
level.Debug(i.logger).Log("msg", "Got no IP address", "instance", s.ID)
continue
}
md1, ok := md[0].(map[string]interface{})
if !ok {
level.Warn(i.logger).Log("msg", "Invalid type for address, expected dict")
continue
for _, address := range md {
md1, ok := address.(map[string]interface{})
if !ok {
level.Warn(i.logger).Log("msg", "Invalid type for address, expected dict")
continue
}
addr, ok := md1["addr"].(string)
if !ok {
level.Warn(i.logger).Log("msg", "Invalid type for address, expected string")
continue
}
if _, ok := floatingIPPresent[addr]; ok {
continue
}
lbls := make(model.LabelSet, len(labels))
for k, v := range labels {
lbls[k] = v
}
lbls[openstackLabelAddressPool] = model.LabelValue(pool)
lbls[openstackLabelPrivateIP] = model.LabelValue(addr)
if val, ok := floatingIPList[floatingIPKey{id: s.ID, fixed: addr}]; ok {
lbls[openstackLabelPublicIP] = model.LabelValue(val)
}
addr = net.JoinHostPort(addr, fmt.Sprintf("%d", i.port))
lbls[model.AddressLabel] = model.LabelValue(addr)
tg.Targets = append(tg.Targets, lbls)
}
addr, ok := md1["addr"].(string)
if !ok {
level.Warn(i.logger).Log("msg", "Invalid type for address, expected string")
continue
}
labels[openstackLabelPrivateIP] = model.LabelValue(addr)
addr = net.JoinHostPort(addr, fmt.Sprintf("%d", i.port))
labels[model.AddressLabel] = model.LabelValue(addr)
// Only use first private IP
break
}
if val, ok := floatingIPList[s.ID]; ok && len(val) > 0 {
labels[openstackLabelPublicIP] = model.LabelValue(val[0])
}
labels[openstackLabelInstanceStatus] = model.LabelValue(s.Status)
labels[openstackLabelInstanceName] = model.LabelValue(s.Name)
id, ok := s.Flavor["id"].(string)
if !ok {
level.Warn(i.logger).Log("msg", "Invalid type for instance id, excepted string")
continue
}
labels[openstackLabelInstanceFlavor] = model.LabelValue(id)
for k, v := range s.Metadata {
name := strutil.SanitizeLabelName(k)
labels[openstackLabelTagPrefix+model.LabelName(name)] = model.LabelValue(v)
}
tg.Targets = append(tg.Targets, labels)
}
return true, nil
})

View File

@ -14,6 +14,7 @@
package openstack
import (
"fmt"
"testing"
"github.com/prometheus/common/model"
@ -56,28 +57,62 @@ func TestOpenstackSDInstanceRefresh(t *testing.T) {
mock := &OpenstackSDInstanceTestSuite{}
mock.SetupTest(t)
instance, _ := mock.openstackAuthSuccess()
instance, err := mock.openstackAuthSuccess()
testutil.Ok(t, err)
tg, err := instance.refresh()
testutil.Ok(t, err)
testutil.Assert(t, tg != nil, "")
testutil.Assert(t, tg.Targets != nil, "")
testutil.Assert(t, len(tg.Targets) == 3, "")
testutil.Equals(t, 4, len(tg.Targets))
testutil.Equals(t, tg.Targets[0]["__address__"], model.LabelValue("10.0.0.32:0"))
testutil.Equals(t, tg.Targets[0]["__meta_openstack_instance_flavor"], model.LabelValue("1"))
testutil.Equals(t, tg.Targets[0]["__meta_openstack_instance_id"], model.LabelValue("ef079b0c-e610-4dfb-b1aa-b49f07ac48e5"))
testutil.Equals(t, tg.Targets[0]["__meta_openstack_instance_name"], model.LabelValue("herp"))
testutil.Equals(t, tg.Targets[0]["__meta_openstack_instance_status"], model.LabelValue("ACTIVE"))
testutil.Equals(t, tg.Targets[0]["__meta_openstack_private_ip"], model.LabelValue("10.0.0.32"))
testutil.Equals(t, tg.Targets[0]["__meta_openstack_public_ip"], model.LabelValue("10.10.10.2"))
testutil.Equals(t, tg.Targets[1]["__address__"], model.LabelValue("10.0.0.31:0"))
testutil.Equals(t, tg.Targets[1]["__meta_openstack_instance_flavor"], model.LabelValue("1"))
testutil.Equals(t, tg.Targets[1]["__meta_openstack_instance_id"], model.LabelValue("9e5476bd-a4ec-4653-93d6-72c93aa682ba"))
testutil.Equals(t, tg.Targets[1]["__meta_openstack_instance_name"], model.LabelValue("derp"))
testutil.Equals(t, tg.Targets[1]["__meta_openstack_instance_status"], model.LabelValue("ACTIVE"))
testutil.Equals(t, tg.Targets[1]["__meta_openstack_private_ip"], model.LabelValue("10.0.0.31"))
for i, lbls := range []model.LabelSet{
model.LabelSet{
"__address__": model.LabelValue("10.0.0.32:0"),
"__meta_openstack_instance_flavor": model.LabelValue("1"),
"__meta_openstack_instance_id": model.LabelValue("ef079b0c-e610-4dfb-b1aa-b49f07ac48e5"),
"__meta_openstack_instance_status": model.LabelValue("ACTIVE"),
"__meta_openstack_instance_name": model.LabelValue("herp"),
"__meta_openstack_private_ip": model.LabelValue("10.0.0.32"),
"__meta_openstack_public_ip": model.LabelValue("10.10.10.2"),
"__meta_openstack_address_pool": model.LabelValue("private"),
},
model.LabelSet{
"__address__": model.LabelValue("10.0.0.31:0"),
"__meta_openstack_instance_flavor": model.LabelValue("1"),
"__meta_openstack_instance_id": model.LabelValue("9e5476bd-a4ec-4653-93d6-72c93aa682ba"),
"__meta_openstack_instance_status": model.LabelValue("ACTIVE"),
"__meta_openstack_instance_name": model.LabelValue("derp"),
"__meta_openstack_private_ip": model.LabelValue("10.0.0.31"),
"__meta_openstack_address_pool": model.LabelValue("private"),
},
model.LabelSet{
"__address__": model.LabelValue("10.0.0.33:0"),
"__meta_openstack_instance_flavor": model.LabelValue("4"),
"__meta_openstack_instance_id": model.LabelValue("9e5476bd-a4ec-4653-93d6-72c93aa682bb"),
"__meta_openstack_instance_status": model.LabelValue("ACTIVE"),
"__meta_openstack_instance_name": model.LabelValue("merp"),
"__meta_openstack_private_ip": model.LabelValue("10.0.0.33"),
"__meta_openstack_address_pool": model.LabelValue("private"),
"__meta_openstack_tag_env": model.LabelValue("prod"),
},
model.LabelSet{
"__address__": model.LabelValue("10.0.0.34:0"),
"__meta_openstack_instance_flavor": model.LabelValue("4"),
"__meta_openstack_instance_id": model.LabelValue("9e5476bd-a4ec-4653-93d6-72c93aa682bb"),
"__meta_openstack_instance_status": model.LabelValue("ACTIVE"),
"__meta_openstack_instance_name": model.LabelValue("merp"),
"__meta_openstack_private_ip": model.LabelValue("10.0.0.34"),
"__meta_openstack_address_pool": model.LabelValue("private"),
"__meta_openstack_tag_env": model.LabelValue("prod"),
"__meta_openstack_public_ip": model.LabelValue("10.10.10.4"),
},
} {
t.Run(fmt.Sprintf("item %d", i), func(t *testing.T) {
testutil.Equals(t, lbls, tg.Targets[i])
})
}
mock.TearDownSuite()
}

View File

@ -327,6 +327,11 @@ const serverListBody = `
"version": 4,
"addr": "10.0.0.32",
"OS-EXT-IPS:type": "fixed"
},
{
"version": 4,
"addr": "10.10.10.2",
"OS-EXT-IPS:type": "floating"
}
]
},
@ -463,10 +468,19 @@ const serverListBody = `
"addresses": {
"private": [
{
"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
"version": 4,
"addr": "10.0.0.31",
"addr": "10.0.0.33",
"OS-EXT-IPS:type": "fixed"
},
{
"version": 4,
"addr": "10.0.0.34",
"OS-EXT-IPS:type": "fixed"
},
{
"version": 4,
"addr": "10.10.10.4",
"OS-EXT-IPS:type": "floating"
}
]
},
@ -488,7 +502,7 @@ const serverListBody = `
"OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
"flavor": {
"id": "1",
"id": "4",
"links": [
{
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
@ -515,7 +529,9 @@ const serverListBody = `
"progress": 0,
"OS-EXT-STS:power_state": 1,
"config_drive": "",
"metadata": {}
"metadata": {
"env": "prod"
}
}
]
}
@ -543,11 +559,18 @@ const listOutput = `
"pool": "nova"
},
{
"fixed_ip": "166.78.185.201",
"fixed_ip": "10.0.0.32",
"id": "2",
"instance_id": "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
"ip": "10.10.10.2",
"pool": "nova"
},
{
"fixed_ip": "10.0.0.34",
"id": "3",
"instance_id": "9e5476bd-a4ec-4653-93d6-72c93aa682bb",
"ip": "10.10.10.4",
"pool": "nova"
}
]
}

View File

@ -477,8 +477,9 @@ The following meta labels are available on targets during [relabeling](#relabel_
#### `instance`
The `instance` role discovers one target per Nova instance. The target
address defaults to the first private IP address of the instance.
The `instance` role discovers one target per network interface of Nova
instance. The target address defaults to the private IP address of the network
interface.
The following meta labels are available on targets during [relabeling](#relabel_config):
@ -488,6 +489,7 @@ The following meta labels are available on targets during [relabeling](#relabel_
* `__meta_openstack_instance_flavor`: the flavor of the OpenStack instance.
* `__meta_openstack_public_ip`: the public IP of the OpenStack instance.
* `__meta_openstack_private_ip`: the private IP of the OpenStack instance.
* `__meta_openstack_address_pool`: the pool of the private IP.
* `__meta_openstack_tag_<tagkey>`: each tag value of the instance.
See below for the configuration options for OpenStack discovery: