Merge "scope for datamodel"

This commit is contained in:
Zuul
2019-03-11 12:40:24 +00:00
committed by Gerrit Code Review
4 changed files with 145 additions and 14 deletions

View File

@@ -53,6 +53,9 @@ class NovaHelper(object):
# We need to pass an object with an 'id' attribute to make it work # We need to pass an object with an 'id' attribute to make it work
return self.nova.hypervisors.get(utils.Struct(id=node_id)) return self.nova.hypervisors.get(utils.Struct(id=node_id))
def get_compute_node_by_name(self, node_name, servers=False):
return self.nova.hypervisors.search(node_name, servers)
def get_compute_node_by_hostname(self, node_hostname): def get_compute_node_by_hostname(self, node_hostname):
"""Get compute node by hostname""" """Get compute node by hostname"""
try: try:

View File

@@ -129,6 +129,7 @@ class BaseClusterDataModelCollector(loadable.LoadableSingleton):
self._cluster_data_model = None self._cluster_data_model = None
self.lock = threading.RLock() self.lock = threading.RLock()
self._audit_scope_handler = None self._audit_scope_handler = None
self._data_model_scope = None
@property @property
def cluster_data_model(self): def cluster_data_model(self):

View File

@@ -165,14 +165,24 @@ class NovaClusterDataModelCollector(base.BaseClusterDataModelCollector):
def get_audit_scope_handler(self, audit_scope): def get_audit_scope_handler(self, audit_scope):
self._audit_scope_handler = compute_scope.ComputeScope( self._audit_scope_handler = compute_scope.ComputeScope(
audit_scope, self.config) audit_scope, self.config)
if self._data_model_scope is None or (
len(self._data_model_scope) > 0 and (
self._data_model_scope != audit_scope)):
self._data_model_scope = audit_scope
self._cluster_data_model = None
LOG.debug("audit scope %s", audit_scope)
return self._audit_scope_handler return self._audit_scope_handler
def execute(self): def execute(self):
"""Build the compute cluster data model""" """Build the compute cluster data model"""
LOG.debug("Building latest Nova cluster data model") LOG.debug("Building latest Nova cluster data model")
if self._audit_scope_handler is None:
LOG.debug("No audit, Don't Build compute data model")
return
builder = ModelBuilder(self.osc) builder = ModelBuilder(self.osc)
return builder.execute() return builder.execute(self._data_model_scope)
class ModelBuilder(object): class ModelBuilder(object):
@@ -196,24 +206,70 @@ class ModelBuilder(object):
def __init__(self, osc): def __init__(self, osc):
self.osc = osc self.osc = osc
self.model = model_root.ModelRoot() self.model = None
self.model_scope = dict()
self.nova = osc.nova() self.nova = osc.nova()
self.nova_helper = nova_helper.NovaHelper(osc=self.osc) self.nova_helper = nova_helper.NovaHelper(osc=self.osc)
# self.neutron = osc.neutron() # self.neutron = osc.neutron()
# self.cinder = osc.cinder() # self.cinder = osc.cinder()
def _collect_aggregates(self, host_aggregates, _nodes):
aggregate_list = self.nova_helper.get_aggregate_list()
aggregate_ids = [aggregate['id'] for aggregate
in host_aggregates if 'id' in aggregate]
aggregate_names = [aggregate['name'] for aggregate
in host_aggregates if 'name' in aggregate]
include_all_nodes = any('*' in field
for field in (aggregate_ids, aggregate_names))
for aggregate in aggregate_list:
if (aggregate.id in aggregate_ids or
aggregate.name in aggregate_names or
include_all_nodes):
_nodes.update(aggregate.hosts)
def _collect_zones(self, availability_zones, _nodes):
service_list = self.nova_helper.get_service_list()
zone_names = [zone['name'] for zone
in availability_zones]
include_all_nodes = False
if '*' in zone_names:
include_all_nodes = True
for service in service_list:
if service.zone in zone_names or include_all_nodes:
_nodes.update(service.host)
def _add_physical_layer(self): def _add_physical_layer(self):
"""Add the physical layer of the graph. """Add the physical layer of the graph.
This includes components which represent actual infrastructure This includes components which represent actual infrastructure
hardware. hardware.
""" """
for cnode in self.nova_helper.get_compute_node_list(): compute_nodes = set()
self.add_compute_node(cnode) host_aggregates = self.model_scope.get("host_aggregates")
availability_zones = self.model_scope.get("availability_zones")
if host_aggregates:
self._collect_aggregates(host_aggregates, compute_nodes)
if availability_zones:
self._collect_zones(availability_zones, compute_nodes)
if not compute_nodes:
all_nodes = self.nova_helper.get_compute_node_list()
compute_nodes = set(
[node.hypervisor_hostname for node in all_nodes])
LOG.debug("compute nodes: %s", compute_nodes)
for node_name in compute_nodes:
cnode = self.nova_helper.get_compute_node_by_name(node_name,
servers=True)
if cnode:
self.add_compute_node(cnode[0])
self.add_instance_node(cnode[0])
def add_compute_node(self, node): def add_compute_node(self, node):
# Build and add base node. # Build and add base node.
compute_node = self.build_compute_node(node) node_info = self.nova_helper.get_compute_node_by_id(node.id)
LOG.debug("node info: %s", node_info)
compute_node = self.build_compute_node(node_info)
self.model.add_node(compute_node) self.model.add_node(compute_node)
# NOTE(v-francoise): we can encapsulate capabilities of the node # NOTE(v-francoise): we can encapsulate capabilities of the node
@@ -242,10 +298,9 @@ class ModelBuilder(object):
:type node: :py:class:`~novaclient.v2.hypervisors.Hypervisor` :type node: :py:class:`~novaclient.v2.hypervisors.Hypervisor`
""" """
# build up the compute node. # build up the compute node.
compute_service = self.nova_helper.get_service(node.service["id"])
node_attributes = { node_attributes = {
"id": node.id, "id": node.id,
"uuid": compute_service.host, "uuid": node.service["host"],
"hostname": node.hypervisor_hostname, "hostname": node.hypervisor_hostname,
"memory": node.memory_mb, "memory": node.memory_mb,
"disk": node.free_disk_gb, "disk": node.free_disk_gb,
@@ -253,7 +308,7 @@ class ModelBuilder(object):
"vcpus": node.vcpus, "vcpus": node.vcpus,
"state": node.state, "state": node.state,
"status": node.status, "status": node.status,
"disabled_reason": compute_service.disabled_reason} "disabled_reason": node.service["disabled_reason"]}
compute_node = element.ComputeNode(**node_attributes) compute_node = element.ComputeNode(**node_attributes)
# compute_node = self._build_node("physical", "compute", "hypervisor", # compute_node = self._build_node("physical", "compute", "hypervisor",
@@ -333,6 +388,29 @@ class ModelBuilder(object):
except exception.ComputeNodeNotFound: except exception.ComputeNodeNotFound:
continue continue
def add_instance_node(self, node):
# node.servers is a list of server objects
# New in nova version 2.53
instances = getattr(node, "servers", None)
if instances is None:
# no instances on this node
return
instance_uuids = [s['uuid'] for s in instances]
for uuid in instance_uuids:
try:
inst = self.nova_helper.find_instance(uuid)
except Exception as exc:
LOG.exception(exc)
continue
# Add Node
instance = self._build_instance_node(inst)
self.model.add_instance(instance)
cnode_uuid = getattr(inst, "OS-EXT-SRV-ATTR:host")
compute_node = self.model.get_node_by_uuid(
cnode_uuid)
# Connect the instance to its compute node
self.model.map_instance(instance, compute_node)
def _build_instance_node(self, instance): def _build_instance_node(self, instance):
"""Build an instance node """Build an instance node
@@ -475,12 +553,53 @@ class ModelBuilder(object):
# node = self._build_node('virtual', 'network', 'network', network) # node = self._build_node('virtual', 'network', 'network', network)
# return network['id'], node # return network['id'], node
def execute(self): def _merge_compute_scope(self, compute_scope):
model_keys = self.model_scope.keys()
update_flag = False
role_keys = ("host_aggregates", "availability_zones")
for role in compute_scope:
role_key = role.keys()[0]
if role_key not in role_keys:
continue
role_values = role.values()[0]
if role_key in model_keys:
for value in role_values:
if value not in self.model_scope[role_key]:
self.model_scope[role_key].sppend(value)
update_flag = True
else:
self.self.model_scope[role_key] = role_values
update_flag = True
return update_flag
def _check_model_scope(self, model_scope):
compute_scope = []
update_flag = False
for _scope in model_scope:
if 'compute' in _scope:
compute_scope = _scope['compute']
break
if self.model_scope:
if model_scope:
if compute_scope:
update_flag = self._merge_compute_scope(compute_scope)
else:
self.model_scope = dict()
update_flag = True
return update_flag
def execute(self, model_scope):
"""Instantiates the graph with the openstack cluster data. """Instantiates the graph with the openstack cluster data.
The graph is populated along 2 layers: virtual and physical. As each The graph is populated along 2 layers: virtual and physical. As each
new layer is built connections are made back to previous layers. new layer is built connections are made back to previous layers.
""" """
self._add_physical_layer() updata_model_flag = self._check_model_scope(model_scope)
self._add_virtual_layer() if self.model is None or updata_model_flag:
self.model = self.model or model_root.ModelRoot()
self._add_physical_layer()
return self.model return self.model

View File

@@ -46,7 +46,8 @@ class TestNovaClusterDataModelCollector(base.TestCase):
fake_compute_node = mock.Mock( fake_compute_node = mock.Mock(
id=1337, id=1337,
service={'id': 123}, service={'id': 123, 'host': 'test_hostname',
'disabled_reason': ''},
hypervisor_hostname='test_hostname', hypervisor_hostname='test_hostname',
memory_mb=333, memory_mb=333,
free_disk_gb=222, free_disk_gb=222,
@@ -54,6 +55,10 @@ class TestNovaClusterDataModelCollector(base.TestCase):
vcpus=4, vcpus=4,
state='TEST_STATE', state='TEST_STATE',
status='TEST_STATUS', status='TEST_STATUS',
servers=[
{'name': 'fake_instance',
'uuid': 'ef500f7e-dac8-470f-960c-169486fce71b'}
]
) )
fake_instance = mock.Mock( fake_instance = mock.Mock(
id='ef500f7e-dac8-470f-960c-169486fce71b', id='ef500f7e-dac8-470f-960c-169486fce71b',
@@ -65,8 +70,10 @@ class TestNovaClusterDataModelCollector(base.TestCase):
setattr(fake_instance, 'OS-EXT-STS:vm_state', 'VM_STATE') setattr(fake_instance, 'OS-EXT-STS:vm_state', 'VM_STATE')
setattr(fake_instance, 'OS-EXT-SRV-ATTR:host', 'test_hostname') setattr(fake_instance, 'OS-EXT-SRV-ATTR:host', 'test_hostname')
m_nova_helper.get_compute_node_list.return_value = [fake_compute_node] m_nova_helper.get_compute_node_list.return_value = [fake_compute_node]
# m_nova_helper.get_instances_by_node.return_value = [fake_instance] m_nova_helper.get_compute_node_by_name.return_value = [
m_nova_helper.get_instance_list.return_value = [fake_instance] fake_compute_node]
m_nova_helper.get_compute_node_by_id.return_value = fake_compute_node
m_nova_helper.find_instance.return_value = fake_instance
m_config = mock.Mock() m_config = mock.Mock()
m_osc = mock.Mock() m_osc = mock.Mock()
@@ -74,6 +81,7 @@ class TestNovaClusterDataModelCollector(base.TestCase):
nova_cdmc = nova.NovaClusterDataModelCollector( nova_cdmc = nova.NovaClusterDataModelCollector(
config=m_config, osc=m_osc) config=m_config, osc=m_osc)
nova_cdmc.get_audit_scope_handler([])
model = nova_cdmc.execute() model = nova_cdmc.execute()
compute_nodes = model.get_all_compute_nodes() compute_nodes = model.get_all_compute_nodes()