Commit a2a78d0a authored by Simon Pasquier's avatar Simon Pasquier Committed by Brian Brazil

discovery/openstack: discover all interfaces (#4649)

* discovery/openstack: discover all interfaces
* Add address pool label
Signed-off-by: 's avatarSimon Pasquier <spasquie@redhat.com>
parent 33c97d73
......@@ -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
}
addr, ok := md1["addr"].(string)
if !ok {
level.Warn(i.logger).Log("msg", "Invalid type for address, expected string")
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)
}
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
})
......
......@@ -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, 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"))
testutil.Equals(t, 4, len(tg.Targets))
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()
}
......@@ -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"
}
]
}
......
......@@ -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:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment