Expand libvirt and ovs plugins to publish entity names

This is useful in dashboards where we display libvirt and ovs
metrics for a physical host, it will elimnate the step of translating
vm and tenant ids if needing to investigate project usage.

The libvirt plugin metadata config array will now support
'vm_name' and 'tenant_name', and the ovs plugin will now
support the metadata config value for 'tenant_name'.

Change-Id: If76418b83119a12b8534fb7f7cb224339f357be8
This commit is contained in:
Brad Klein 2016-09-23 09:18:16 -06:00 committed by Brad Klein
parent 0db9a7202e
commit 71a0d5dd26
6 changed files with 133 additions and 36 deletions

View File

@ -16,6 +16,10 @@ init_config:
# If set, will submit raw counters from ovs-vsctl command output for the given
# network interface
use_absolute_metrics: true
# List of router metadata keys to be sent as dimensions when cross posting
# metrics to the infrastruture project.
metadata:
- tenant_name
# Installations that don't allow usage of sudo should copy the `ovs-vsctl`
# command to another location and use the `setcap` command to allow the
# monasca-agent to run that command. The new location of the `ovs-vsctl`

View File

@ -39,9 +39,19 @@ The Libvirt plugin uses a cache directory to persist data, which is `/dev/shm` b
If the owner of the VM is in a different tenant the Agent Cross-Tenant Metric Submission can be setup. See this [documentation](https://github.com/openstack/monasca-agent/blob/master/docs/MonascaMetrics.md#cross-tenant-metric-submission) for details.
`admin_user` is the username capable of making administrative nova calls.
`admin_password` password for the nova user.
`admin_tenant_name` is the project/tenant to POST metrics with the `vm.` prefix.
`identity_url` is the keystone endpoint for auth.
`region_name` is used to add the region dimension to metrics.
`nova_refresh` specifies the number of seconds between calls to the Nova API to refresh the instance cache. This is helpful for updating VM hostname and pruning deleted instances from the cache. By default, it is set to 14,400 seconds (four hours). Set to 0 to refresh every time the Collector runs, or to None to disable regular refreshes entirely (though the instance cache will still be refreshed if a new instance is detected).
`metadata` specifies the list of instance metadata keys to be included as dimensions with the cross-tenant metrics for the operations project. This is helpful to give more information about an instance. When using the agent setup scripts, by default `scale_group` metadata is enabled for supporting auto scaling in Heat.
`metadata` specifies the list of instance metadata keys to be included as dimensions with the cross-tenant metrics for the operations project. This is helpful to give more information about an instance. When using the agent setup scripts, by default `scale_group` metadata is enabled for supporting auto scaling in Heat. VM name and tenant name (in addition to default IDs) can be provided as dimensions if `vm_name` and `tenant_name` are provided in the list of metadata keys.
`customer_metadata` specifies the list of instance metadata keys to be included as dimensions with customer metrics. This is helpful to give more information about an instance.
@ -59,7 +69,6 @@ If the owner of the VM is in a different tenant the Agent Cross-Tenant Metric Su
`vnic_collection_period` will cause vnic metrics to be output at a minimum `vnic_collection_period` second interval. This can be optionally set to have vnic metrics be outputted less often to reduce metric load on the system. If this is less than the agent collection period, it will be ignored. The default value is 0.
`vm_cpu_check_enable` enables collecting of VM CPU metrics (Default True). Please see "Mapping Metrics to Configuration Parameters" section below for what metrics are controlled by this flag.
`vm_disks_check_enable` enables collecting of VM Disk metrics (Default True). Please see "Mapping Metrics to Configuration Parameters" section below for what metrics are controlled by this flag.
@ -411,16 +420,18 @@ Please see table below for metrics in libvirt that are always enabled.
## VM Dimensions
All metrics include `resource_id` and `zone` (availability zone) dimensions. Because there is a separate set of metrics for the two target audiences (VM customers and Operations), other dimensions may differ.
| Dimension Name | Customer Value | Operations Value |
| -------------- | ------------------------- | ----------------------- |
| hostname | name of VM as provisioned | hypervisor's hostname |
| zone | availability zone | availability zone |
| resource_id | resource ID of VM | resource ID of VM |
| service | "compute" | "compute" |
| component | "vm" | "vm" |
| device | name of net or disk dev | name of net or disk dev |
| port_id | port ID of the VM port | port ID of the VM port |
| tenant_id | (N/A) | owner of VM |
| Dimension Name | Customer Value | Operations Value |
| -------------- | ------------------------- | -------------------------------------------------------------- |
| hostname | name of VM as provisioned | hypervisor's hostname |
| zone | availability zone | availability zone |
| resource_id | resource ID of VM | resource ID of VM |
| service | "compute" | "compute" |
| component | "vm" | "vm" |
| device | name of net or disk dev | name of net or disk dev |
| port_id | port ID of the VM port | port ID of the VM port |
| tenant_id | (N/A) | owner of VM |
| tenant_name | (N/A) | name of the project owner of the VM (if configured to publish) |
| vm_name | (N/A) | name of the VM (if configured to publish) |
## Aggregate Metrics

View File

@ -32,7 +32,7 @@ configured using the configuration file example below.
`admin_password` password for the neutron user.
`admin_tenant_name` is the project/tenant to POST metrics with the `vm.` prefix.
`admin_tenant_name` is the project/tenant to POST metrics with the `ovs.` prefix.
`admin_user` is the username capable of making administrative neutron calls.
@ -48,6 +48,8 @@ configured using the configuration file example below.
`check_router_ha` will check router HA status if set to true. This should be set to false if not configuring routers for HA, as setting this to true will cause the plugin to make additional neutron calls.
`metadata` specifies the list of router metadata keys to be included as dimensions with the cross-tenant metrics for the operations project. This is helpful to give more information about an router. Tenant name (in addition to default ID) can be provided as dimensions if `tenant_name` is provided in the list of metadata keys.
`ovs_cmd` is the location of the open vswitch command. Installations that allow sudo should set this to `sudo /usr/bin/ovs-vsctl` and add `mon-agent ALL=(ALL) NOPASSWD:/usr/bin/ovs-vsctl` to the `/etc/sudoers` file. Installations that don't allow usage of sudo should copy the `ovs-vsctl` command to another location and use the `setcap` command to allow the monasca-agent to run that command. The new location of the `ovs-vsctl` command should be what is set in the config file for `ovs_cmd`.
`instances` are not used and should be empty in `ovs.yaml` because like the ovs plugin it runs against all routers hosted on the node at once.
@ -173,25 +175,27 @@ NOTE:
## Router Metric Dimensions
| Dimension Name | Customer Value | Operations Value |
| -------------- | -------------------------- | --------------------------- |
| hostname | (N/A) | hostname hosting the router |
| resource_id | resource ID of router | resource ID of router |
| service | "networking" | "networking" |
| component | "ovs" | "ovs" |
| router_name | name of the virtual router | name of the virtual router |
| tenant_id | (N/A) | project owner of the router |
| port_id | port ID of the router | port ID of the router |
| Dimension Name | Customer Value | Operations Value |
| -------------- | -------------------------- | ------------------------------------------------------------------ |
| hostname | (N/A) | hostname hosting the router |
| resource_id | resource ID of router | resource ID of router |
| service | "networking" | "networking" |
| component | "ovs" | "ovs" |
| router_name | name of the virtual router | name of the virtual router |
| tenant_id | (N/A) | id of the project owner of the router |
| tenant_name | (N/A) | name of the project owner of the router (if configured to publish) |
| port_id | port ID of the router | port ID of the router |
## OVS Port Metric Dimensions
| Dimension Name | Customer Value | Operations Value |
| -------------- | -------------------------- | --------------------------- |
| hostname | (N/A) | hostname hosting the ports |
| resource_id | resource ID of port | resource id of the port |
| service | "networking" | "networking" |
| component | "ovs" | "ovs" |
| tenant_id | (N/A) | project owner of the port |
| port_id | port ID of VM | port ID of VM |
| Dimension Name | Customer Value | Operations Value |
| -------------- | -------------------------- | ------------------------------------------------------------------ |
| hostname | (N/A) | hostname hosting the ports |
| resource_id | resource ID of port | resource id of the port |
| service | "networking" | "networking" |
| component | "ovs" | "ovs" |
| tenant_id | (N/A) | project owner of the port |
| tenant_name | (N/A) | name of the project owner of the router (if configured to publish) |
| port_id | port ID of VM | port ID of VM |
# License
(C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP

View File

@ -11,3 +11,39 @@ def add_basic_auth(request, username, password):
auth_str = base64.encodestring('%s:%s' % (username, password)).strip()
request.add_header('Authorization', 'Basic %s' % auth_str)
return request
def get_keystone_client(config):
import keystoneclient.v2_0.client as kc
kwargs = {
'username': config.get('admin_user'),
'project_name': config.get('admin_tenant_name'),
'password': config.get('admin_password'),
'auth_url': config.get('identity_uri'),
'endpoint_type': 'internalURL',
'region_name': config.get('region_name'),
}
return kc.Client(**kwargs)
def get_tenant_name(tenants, tenant_id):
tenant_name = None
for tenant in tenants:
if tenant.id == tenant_id:
tenant_name = tenant.name
break
return tenant_name
def get_tenant_list(config, log):
tenants = []
try:
log.debug("Retrieving Keystone tenant list")
keystone = get_keystone_client(config)
tenants = keystone.tenants.list()
except Exception as e:
msg = "Unable to get tenant list from keystone: {0}"
log.error(msg.format(e))
return tenants

View File

@ -18,6 +18,7 @@
import json
import libvirt
import math
import monasca_agent.collector.checks.utils as utils
import os
import stat
import subprocess
@ -145,6 +146,14 @@ class LibvirtCheck(AgentCheck):
self.log.warn("Unable to ping VMs, no network namespaces found." +
"Either no VMs are present, or routing is centralized.")
#
# Only make the keystone call to get the tenant list
# if we are configured to publish tenant names.
#
tenants = []
if self.init_config.get('metadata') and 'tenant_name' in self.init_config.get('metadata'):
tenants = utils.get_tenant_list(self.init_config, self.log)
for instance in instances:
instance_ports = []
inst_name = instance.__getattr__('OS-EXT-SRV-ATTR:instance_name')
@ -170,6 +179,10 @@ class LibvirtCheck(AgentCheck):
'disk': inst_flavor.disk,
'instance_ports': instance_ports}
tenant_name = utils.get_tenant_name(tenants, instance.tenant_id)
if tenant_name:
id_cache[inst_name]['tenant_name'] = tenant_name
for config_var in ['metadata', 'customer_metadata']:
if self.init_config.get(config_var):
for metadata in self.init_config.get(config_var):
@ -671,12 +684,7 @@ class LibvirtCheck(AgentCheck):
# Add dimensions that would be helpful for operations
dims_operations = dims_customer.copy()
dims_operations['tenant_id'] = instance_cache.get(inst_name)['tenant_id']
if self.init_config.get('metadata'):
for metadata in self.init_config.get('metadata'):
metadata_value = (instance_cache.get(inst_name).
get(metadata))
if metadata_value:
dims_operations[metadata] = metadata_value
dims_operations = self._update_dims_with_metadata(instance_cache, inst_name, dims_operations)
if self.init_config.get('customer_metadata'):
for metadata in self.init_config.get('customer_metadata'):
metadata_value = (instance_cache.get(inst_name).
@ -779,3 +787,18 @@ class LibvirtCheck(AgentCheck):
#
rate_value = -1
return rate_value
def _update_dims_with_metadata(self, instance_cache, inst_name, dim_operations):
"""Update operations dimensions with metadata."""
dims = dim_operations
if self.init_config.get('metadata'):
for metadata in self.init_config.get('metadata'):
if 'vm_name' == metadata:
metadata_value = (instance_cache.get(inst_name).
get('hostname'))
else:
metadata_value = (instance_cache.get(inst_name).
get(metadata))
if metadata_value:
dims[metadata] = metadata_value
return dims

View File

@ -6,6 +6,7 @@ import datetime
import json
import logging
import math
import monasca_agent.collector.checks.utils as utils
import os
import re
import socket
@ -154,6 +155,9 @@ class OvsCheck(AgentCheck):
device_uuid = port_info['device_uuid']
is_router_port = port_info['is_router_port']
tenant_id = port_info['tenant_id']
tenant_name = None
if 'tenant_name' in port_info:
tenant_name = port_info['tenant_name']
if is_router_port and not self._is_active_router(device_uuid):
continue
if is_router_port:
@ -193,6 +197,8 @@ class OvsCheck(AgentCheck):
continue
ops_dimensions = this_dimensions.copy()
ops_dimensions.update({'tenant_id': tenant_id})
if tenant_name:
ops_dimensions.update({'tenant_name': tenant_name})
if self.use_rate_metrics:
self.gauge(metric_name_rate, value[interface_stats_key],
dimensions=customer_dimensions,
@ -393,6 +399,15 @@ class OvsCheck(AgentCheck):
all_ports_data = all_ports_data['ports']
all_routers_data = all_routers_data['routers']
#
# Only make the keystone call to get the tenant list
# if we are configured to publish tenant names.
#
if self.init_config.get('metadata') and 'tenant_name' in self.init_config.get('metadata'):
tenants = utils.get_tenant_list(self.init_config, self.log)
else:
tenants = []
for port_data in all_ports_data:
port_uuid = port_data['id']
device_uuid = port_data['device_id']
@ -410,6 +425,10 @@ class OvsCheck(AgentCheck):
'is_router_port': is_router_port,
'tenant_id': tenant_id}
tenant_name = utils.get_tenant_name(tenants, tenant_id)
if tenant_name:
port_cache[port_uuid]['tenant_name'] = tenant_name
port_cache['last_update'] = int(time.time())
# Write the updated cache