Merge "Graph cluster model instead of mapping one"

This commit is contained in:
Jenkins 2017-01-25 13:54:27 +00:00 committed by Gerrit Code Review
commit e3c6db11c6
44 changed files with 1001 additions and 993 deletions

View File

@ -41,3 +41,5 @@ stevedore>=1.17.1 # Apache-2.0
taskflow>=2.7.0 # Apache-2.0
WebOb>=1.6.0 # MIT
WSME>=0.8 # MIT
networkx>=1.10 # BSD

View File

@ -64,6 +64,15 @@ class NovaHelper(object):
LOG.exception(exc)
raise exception.ComputeNodeNotFound(name=node_hostname)
def get_instance_list(self):
return self.nova.servers.list(search_opts={'all_tenants': True})
def get_service(self, service_id):
return self.nova.services.find(id=service_id)
def get_flavor(self, flavor_id):
return self.nova.flavors.get(flavor_id)
def get_aggregate_list(self):
return self.nova.aggregates.list()

View File

@ -34,3 +34,7 @@ class Model(object):
@abc.abstractmethod
def to_string(self):
raise NotImplementedError()
@abc.abstractmethod
def to_xml(self):
raise NotImplementedError()

View File

@ -1,23 +1,21 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
# Copyright (c) 2017 Intel Innovation and Research Ireland Ltd.
#
# 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
# 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.
# 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.
from oslo_log import log
from watcher.common import exception
from watcher.common import nova_helper
from watcher.decision_engine.model.collector import base
from watcher.decision_engine.model import element
@ -30,13 +28,12 @@ LOG = log.getLogger(__name__)
class NovaClusterDataModelCollector(base.BaseClusterDataModelCollector):
"""Nova cluster data model collector
The Nova cluster data model collector creates an in-memory
representation of the resources exposed by the compute service.
The Nova cluster data model collector creates an in-memory
representation of the resources exposed by the compute service.
"""
def __init__(self, config, osc=None):
super(NovaClusterDataModelCollector, self).__init__(config, osc)
self.wrapper = nova_helper.NovaHelper(osc=self.osc)
@property
def notification_endpoints(self):
@ -62,49 +59,313 @@ class NovaClusterDataModelCollector(base.BaseClusterDataModelCollector):
"""Build the compute cluster data model"""
LOG.debug("Building latest Nova cluster data model")
model = model_root.ModelRoot()
mem = element.Resource(element.ResourceType.memory)
num_cores = element.Resource(element.ResourceType.cpu_cores)
disk = element.Resource(element.ResourceType.disk)
disk_capacity = element.Resource(element.ResourceType.disk_capacity)
model.create_resource(mem)
model.create_resource(num_cores)
model.create_resource(disk)
model.create_resource(disk_capacity)
builder = ModelBuilder(self.osc)
return builder.execute()
flavor_cache = {}
nodes = self.wrapper.get_compute_node_list()
for n in nodes:
service = self.wrapper.nova.services.find(id=n.service['id'])
# create node in cluster_model_collector
node = element.ComputeNode(n.id)
node.uuid = service.host
node.hostname = n.hypervisor_hostname
# set capacity
mem.set_capacity(node, n.memory_mb)
disk.set_capacity(node, n.free_disk_gb)
disk_capacity.set_capacity(node, n.local_gb)
num_cores.set_capacity(node, n.vcpus)
node.state = n.state
node.status = n.status
model.add_node(node)
instances = self.wrapper.get_instances_by_node(str(service.host))
for v in instances:
# create VM in cluster_model_collector
instance = element.Instance()
instance.uuid = v.id
# nova/nova/compute/instance_states.py
instance.state = getattr(v, 'OS-EXT-STS:vm_state')
# set capacity
self.wrapper.get_flavor_instance(v, flavor_cache)
mem.set_capacity(instance, v.flavor['ram'])
# FIXME: update all strategies to use disk_capacity
# for instances instead of disk
disk.set_capacity(instance, v.flavor['disk'])
disk_capacity.set_capacity(instance, v.flavor['disk'])
num_cores.set_capacity(instance, v.flavor['vcpus'])
class ModelBuilder(object):
"""Build the graph-based model
model.map_instance(instance, node)
This model builder adds the following data"
return model
- Compute-related knowledge (Nova)
- TODO(v-francoise): Storage-related knowledge (Cinder)
- TODO(v-francoise): Network-related knowledge (Neutron)
NOTE(v-francoise): This model builder is meant to be extended in the future
to also include both storage and network information respectively coming
from Cinder and Neutron. Some prelimary work has been done in this
direction in https://review.openstack.org/#/c/362730 but since we cannot
guarantee a sufficient level of consistency for neither the storage nor the
network part before the end of the Ocata cycle, this work has been
re-scheduled for Pike. In the meantime, all the associated code has been
commented out.
"""
def __init__(self, osc):
self.osc = osc
self.model = model_root.ModelRoot()
self.nova = osc.nova()
self.nova_helper = nova_helper.NovaHelper(osc=self.osc)
# self.neutron = osc.neutron()
# self.cinder = osc.cinder()
def _add_physical_layer(self):
"""Add the physical layer of the graph.
This includes components which represent actual infrastructure
hardware.
"""
for cnode in self.nova_helper.get_compute_node_list():
self.add_compute_node(cnode)
def add_compute_node(self, node):
# Build and add base node.
compute_node = self.build_compute_node(node)
self.model.add_node(compute_node)
# NOTE(v-francoise): we can encapsulate capabilities of the node
# (special instruction sets of CPUs) in the attributes; as well as
# sub-nodes can be added re-presenting e.g. GPUs/Accelerators etc.
# # Build & add disk, memory, network and cpu nodes.
# disk_id, disk_node = self.build_disk_compute_node(base_id, node)
# self.add_node(disk_id, disk_node)
# mem_id, mem_node = self.build_memory_compute_node(base_id, node)
# self.add_node(mem_id, mem_node)
# net_id, net_node = self._build_network_compute_node(base_id)
# self.add_node(net_id, net_node)
# cpu_id, cpu_node = self.build_cpu_compute_node(base_id, node)
# self.add_node(cpu_id, cpu_node)
# # Connect the base compute node to the dependant nodes.
# self.add_edges_from([(base_id, disk_id), (base_id, mem_id),
# (base_id, cpu_id), (base_id, net_id)],
# label="contains")
def build_compute_node(self, node):
"""Build a compute node from a Nova compute node
:param node: A node hypervisor instance
:type node: :py:class:`~novaclient.v2.hypervisors.Hypervisor`
"""
# build up the compute node.
compute_service = self.nova_helper.get_service(node.service["id"])
node_attributes = {
"id": node.id,
"human_id": None, # TODO(v-francoise): get rid of it
"uuid": compute_service.host,
"hostname": node.hypervisor_hostname,
"memory": node.memory_mb,
"disk": node.free_disk_gb,
"disk_capacity": node.local_gb,
"vcpus": node.vcpus,
"state": node.state,
"status": node.status}
compute_node = element.ComputeNode(**node_attributes)
# compute_node = self._build_node("physical", "compute", "hypervisor",
# node_attributes)
return compute_node
# def _build_network_compute_node(self, base_node):
# attributes = {}
# net_node = self._build_node("physical", "network", "NIC", attributes)
# net_id = "{}_network".format(base_node)
# return net_id, net_node
# def build_disk_compute_node(self, base_node, compute):
# # Build disk node attributes.
# disk_attributes = {
# "size_gb": compute.local_gb,
# "used_gb": compute.local_gb_used,
# "available_gb": compute.free_disk_gb}
# disk_node = self._build_node("physical", "storage", "disk",
# disk_attributes)
# disk_id = "{}_disk".format(base_node)
# return disk_id, disk_node
# def build_memory_compute_node(self, base_node, compute):
# # Build memory node attributes.
# memory_attrs = {"size_mb": compute.memory_mb,
# "used_mb": compute.memory_mb_used,
# "available_mb": compute.free_ram_mb}
# memory_node = self._build_node("physical", "memory", "memory",
# memory_attrs)
# memory_id = "{}_memory".format(base_node)
# return memory_id, memory_node
# def build_cpu_compute_node(self, base_node, compute):
# # Build memory node attributes.
# cpu_attributes = {"vcpus": compute.vcpus,
# "vcpus_used": compute.vcpus_used,
# "info": jsonutils.loads(compute.cpu_info)}
# cpu_node = self._build_node("physical", "cpu", "cpu", cpu_attributes)
# cpu_id = "{}_cpu".format(base_node)
# return cpu_id, cpu_node
# @staticmethod
# def _build_node(layer, category, node_type, attributes):
# return {"layer": layer, "category": category, "type": node_type,
# "attributes": attributes}
def _add_virtual_layer(self):
"""Add the virtual layer to the graph.
This layer is the virtual components of the infrastructure,
such as vms.
"""
self._add_virtual_servers()
# self._add_virtual_network()
# self._add_virtual_storage()
def _add_virtual_servers(self):
all_instances = self.nova_helper.get_instance_list()
for inst in all_instances:
# Add Node
instance = self._build_instance_node(inst)
self.model.add_instance(instance)
# Get the cnode_name uuid.
cnode_uuid = getattr(inst, "OS-EXT-SRV-ATTR:host")
if cnode_uuid is None:
# The instance is not attached to any Compute node
continue
try:
# Nova compute node
# cnode = self.nova_helper.get_compute_node_by_hostname(
# cnode_uuid)
compute_node = self.model.get_node_by_uuid(
cnode_uuid)
# Connect the instance to its compute node
self.model.add_edge(
instance, compute_node, label='RUNS_ON')
except exception.ComputeNodeNotFound:
continue
def _build_instance_node(self, instance):
"""Build an instance node
Create an instance node for the graph using nova and the
`server` nova object.
:param instance: Nova VM object.
:return: A instance node for the graph.
"""
flavor = self.nova_helper.get_flavor(instance.flavor["id"])
instance_attributes = {
"uuid": instance.id,
"human_id": instance.human_id,
"memory": flavor.ram,
"disk": flavor.disk,
"disk_capacity": flavor.disk,
"vcpus": flavor.vcpus,
"state": getattr(instance, "OS-EXT-STS:vm_state")}
# node_attributes = dict()
# node_attributes["layer"] = "virtual"
# node_attributes["category"] = "compute"
# node_attributes["type"] = "compute"
# node_attributes["attributes"] = instance_attributes
return element.Instance(**instance_attributes)
# def _add_virtual_storage(self):
# try:
# volumes = self.cinder.volumes.list()
# except Exception:
# return
# for volume in volumes:
# volume_id, volume_node = self._build_storage_node(volume)
# self.add_node(volume_id, volume_node)
# host = self._get_volume_host_id(volume_node)
# self.add_edge(volume_id, host)
# # Add connections to an instance.
# if volume_node['attributes']['attachments']:
# for attachment in volume_node['attributes']['attachments']:
# self.add_edge(volume_id, attachment['server_id'],
# label='ATTACHED_TO')
# volume_node['attributes'].pop('attachments')
# def _add_virtual_network(self):
# try:
# routers = self.neutron.list_routers()
# except Exception:
# return
# for network in self.neutron.list_networks()['networks']:
# self.add_node(*self._build_network(network))
# for router in routers['routers']:
# self.add_node(*self._build_router(router))
# router_interfaces, _, compute_ports = self._group_ports()
# for router_interface in router_interfaces:
# interface = self._build_router_interface(router_interface)
# router_interface_id = interface[0]
# router_interface_node = interface[1]
# router_id = interface[2]
# self.add_node(router_interface_id, router_interface_node)
# self.add_edge(router_id, router_interface_id)
# network_id = router_interface_node['attributes']['network_id']
# self.add_edge(router_interface_id, network_id)
# for compute_port in compute_ports:
# cp_id, cp_node, instance_id = self._build_compute_port_node(
# compute_port)
# self.add_node(cp_id, cp_node)
# self.add_edge(cp_id, vm_id)
# net_id = cp_node['attributes']['network_id']
# self.add_edge(net_id, cp_id)
# # Connect port to physical node
# phys_net_node = "{}_network".format(cp_node['attributes']
# ['binding:host_id'])
# self.add_edge(cp_id, phys_net_node)
# def _get_volume_host_id(self, volume_node):
# host = volume_node['attributes']['os-vol-host-attr:host']
# if host.find('@') != -1:
# host = host.split('@')[0]
# elif host.find('#') != -1:
# host = host.split('#')[0]
# return "{}_disk".format(host)
# def _build_storage_node(self, volume_obj):
# volume = volume_obj.__dict__
# volume["name"] = volume["id"]
# volume.pop("id")
# volume.pop("manager")
# node = self._build_node("virtual", "storage", 'volume', volume)
# return volume["name"], node
# def _build_compute_port_node(self, compute_port):
# compute_port["name"] = compute_port["id"]
# compute_port.pop("id")
# nde_type = "{}_port".format(
# compute_port["device_owner"].split(":")[0])
# compute_port.pop("device_owner")
# device_id = compute_port["device_id"]
# compute_port.pop("device_id")
# node = self._build_node("virtual", "network", nde_type, compute_port)
# return compute_port["name"], node, device_id
# def _group_ports(self):
# router_interfaces = []
# floating_ips = []
# compute_ports = []
# interface_types = ["network:router_interface",
# 'network:router_gateway']
# for port in self.neutron.list_ports()['ports']:
# if port['device_owner'] in interface_types:
# router_interfaces.append(port)
# elif port['device_owner'].startswith('compute:'):
# compute_ports.append(port)
# elif port['device_owner'] == 'network:floatingip':
# floating_ips.append(port)
# return router_interfaces, floating_ips, compute_ports
# def _build_router_interface(self, interface):
# interface["name"] = interface["id"]
# interface.pop("id")
# node_type = interface["device_owner"].split(":")[1]
# node = self._build_node("virtual", "network", node_type, interface)
# return interface["name"], node, interface["device_id"]
# def _build_router(self, router):
# router_attrs = {"uuid": router['id'],
# "name": router['name'],
# "state": router['status']}
# node = self._build_node('virtual', 'network', 'router', router_attrs)
# return str(router['id']), node
# def _build_network(self, network):
# node = self._build_node('virtual', 'network', 'network', network)
# return network['id'], node
def execute(self):
"""Instantiates the graph with the openstack cluster data.
The graph is populated along 2 layers: virtual and physical. As each
new layer is built connections are made back to previous layers.
"""
self._add_physical_layer()
self._add_virtual_layer()
return self.model

View File

@ -17,13 +17,51 @@
# limitations under the License.
import abc
import collections
from lxml import etree
from oslo_log import log
import six
from watcher.objects import base
from watcher.objects import fields as wfields
LOG = log.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class Element(object):
class Element(base.WatcherObject, base.WatcherObjectDictCompat):
# Initial version
VERSION = '1.0'
fields = {}
def __init__(self, context=None, **kwargs):
for name, field in self.fields.items():
# The idea here is to force the initialization of unspecified
# fields that have a default value
if (name not in kwargs and not field.nullable and
field.default != wfields.UnspecifiedDefault):
kwargs[name] = field.default
super(Element, self).__init__(context, **kwargs)
@abc.abstractmethod
def accept(self, visitor):
raise NotImplementedError()
def as_xml_element(self):
sorted_fieldmap = []
for field in self.fields:
try:
value = str(self[field])
sorted_fieldmap.append((field, value))
except Exception as exc:
LOG.exception(exc)
attrib = collections.OrderedDict(sorted_fieldmap)
element_name = self.__class__.__name__
instance_el = etree.Element(element_name, attrib=attrib)
return instance_el

View File

@ -19,39 +19,15 @@ import abc
import six
from watcher.decision_engine.model.element import base
from watcher.objects import fields as wfields
@six.add_metaclass(abc.ABCMeta)
class ComputeResource(base.Element):
def __init__(self):
self._uuid = ""
self._human_id = ""
self._hostname = ""
VERSION = '1.0'
@property
def uuid(self):
return self._uuid
@uuid.setter
def uuid(self, u):
self._uuid = u
@property
def hostname(self):
return self._hostname
@hostname.setter
def hostname(self, h):
self._hostname = h
@property
def human_id(self):
return self._human_id
@human_id.setter
def human_id(self, h):
self._human_id = h
def __str__(self):
return "[{0}]".format(self.uuid)
fields = {
"uuid": wfields.StringField(),
"human_id": wfields.StringField(default=""),
}

View File

@ -17,6 +17,8 @@
import enum
from watcher.decision_engine.model.element import compute_resource
from watcher.objects import base
from watcher.objects import fields as wfields
class InstanceState(enum.Enum):
@ -36,19 +38,17 @@ class InstanceState(enum.Enum):
ERROR = 'error'
@base.WatcherObjectRegistry.register_if(False)
class Instance(compute_resource.ComputeResource):
def __init__(self):
super(Instance, self).__init__()
self._state = InstanceState.ACTIVE.value
fields = {
"state": wfields.StringField(default=InstanceState.ACTIVE.value),
"memory": wfields.NonNegativeIntegerField(),
"disk": wfields.IntegerField(),
"disk_capacity": wfields.NonNegativeIntegerField(),
"vcpus": wfields.NonNegativeIntegerField(),
}
def accept(self, visitor):
raise NotImplementedError()
@property
def state(self):
return self._state
@state.setter
def state(self, state):
self._state = state

View File

@ -17,6 +17,8 @@
import enum
from watcher.decision_engine.model.element import compute_resource
from watcher.objects import base
from watcher.objects import fields as wfields
class ServiceState(enum.Enum):
@ -26,29 +28,20 @@ class ServiceState(enum.Enum):
DISABLED = 'disabled'
@base.WatcherObjectRegistry.register_if(False)
class ComputeNode(compute_resource.ComputeResource):
def __init__(self, id):
super(ComputeNode, self).__init__()
self.id = id
self._state = ServiceState.ONLINE.value
self._status = ServiceState.ENABLED.value
fields = {
"id": wfields.NonNegativeIntegerField(),
"hostname": wfields.StringField(),
"status": wfields.StringField(default=ServiceState.ENABLED.value),
"state": wfields.StringField(default=ServiceState.ONLINE.value),
"memory": wfields.NonNegativeIntegerField(),
"disk": wfields.IntegerField(),
"disk_capacity": wfields.NonNegativeIntegerField(),
"vcpus": wfields.NonNegativeIntegerField(),
}
def accept(self, visitor):
raise NotImplementedError()
@property
def state(self):
return self._state
@state.setter
def state(self, state):
self._state = state
@property
def status(self):
return self._status
@status.setter
def status(self, s):
self._status = s

View File

@ -20,7 +20,8 @@ from watcher.common import exception
class ResourceType(enum.Enum):
cpu_cores = 'num_cores'
cpu_cores = 'vcpus'
vcpus = 'vcpus'
memory = 'memory'
disk = 'disk'
disk_capacity = 'disk_capacity'

View File

@ -1,101 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# 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.
from oslo_concurrency import lockutils
from oslo_log import log
from watcher._i18n import _LW
LOG = log.getLogger(__name__)
class Mapping(object):
def __init__(self, model):
self.model = model
self.compute_node_mapping = {}
self.instance_mapping = {}
def map(self, node, instance):
"""Select the node where the instance is launched
:param node: the node
:param instance: the virtual machine or instance
"""
with lockutils.lock(__name__):
# init first
if node.uuid not in self.compute_node_mapping.keys():
self.compute_node_mapping[node.uuid] = set()
# map node => instances
self.compute_node_mapping[node.uuid].add(instance.uuid)
# map instance => node
self.instance_mapping[instance.uuid] = node.uuid
def unmap(self, node, instance):
"""Remove the instance from the node
:param node: the node
:param instance: the virtual machine or instance
"""
self.unmap_by_uuid(node.uuid, instance.uuid)
def unmap_by_uuid(self, node_uuid, instance_uuid):
"""Remove the instance (by id) from the node (by id)
:rtype : object
"""
with lockutils.lock(__name__):
if str(node_uuid) in self.compute_node_mapping:
self.compute_node_mapping[str(node_uuid)].remove(
str(instance_uuid))
# remove instance
self.instance_mapping.pop(instance_uuid)
else:
LOG.warning(
_LW("Trying to delete the instance %(instance)s but it "
"was not found on node %(node)s") %
{'instance': instance_uuid, 'node': node_uuid})
def get_mapping(self):
return self.compute_node_mapping
def get_node_from_instance(self, instance):
return self.get_node_by_instance_uuid(instance.uuid)
def get_node_by_instance_uuid(self, instance_uuid):
"""Getting host information from the guest instance
:param instance: the uuid of the instance
:return: node
"""
return self.model.get_node_by_uuid(
self.instance_mapping[str(instance_uuid)])
def get_node_instances(self, node):
"""Get the list of instances running on the node
:param node:
:return:
"""
return self.get_node_instances_by_uuid(node.uuid)
def get_node_instances_by_uuid(self, node_uuid):
if str(node_uuid) in self.compute_node_mapping.keys():
return self.compute_node_mapping[str(node_uuid)]
else:
# empty
return set()

View File

@ -1,39 +1,40 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
# Copyright (c) 2016 Intel Innovation and Research Ireland Ltd.
#
# 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
# 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.
# 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.
import collections
"""
Openstack implementation of the cluster graph.
"""
from lxml import etree
import networkx as nx
from oslo_concurrency import lockutils
from oslo_log import log
import six
from watcher._i18n import _
from watcher.common import exception
from watcher.common import utils
from watcher.decision_engine.model import base
from watcher.decision_engine.model import element
from watcher.decision_engine.model import mapping
LOG = log.getLogger(__name__)
class ModelRoot(base.Model):
class ModelRoot(nx.DiGraph, base.Model):
"""Cluster graph for an Openstack cluster."""
def __init__(self, stale=False):
self._nodes = utils.Struct()
self._instances = utils.Struct()
self.mapping = mapping.Mapping(self)
self.resource = utils.Struct()
super(ModelRoot, self).__init__()
self.stale = stale
def __nonzero__(self):
@ -41,35 +42,47 @@ class ModelRoot(base.Model):
__bool__ = __nonzero__
def assert_node(self, obj):
@staticmethod
def assert_node(obj):
if not isinstance(obj, element.ComputeNode):
raise exception.IllegalArgumentException(
message=_("'obj' argument type is not valid"))
message=_("'obj' argument type is not valid: %s") % type(obj))
def assert_instance(self, obj):
@staticmethod
def assert_instance(obj):
if not isinstance(obj, element.Instance):
raise exception.IllegalArgumentException(
message=_("'obj' argument type is not valid"))
@lockutils.synchronized("model_root")
def add_node(self, node):
self.assert_node(node)
self._nodes[node.uuid] = node
super(ModelRoot, self).add_node(node)
@lockutils.synchronized("model_root")
def remove_node(self, node):
self.assert_node(node)
if str(node.uuid) not in self._nodes:
try:
super(ModelRoot, self).remove_node(node)
except nx.NetworkXError as exc:
LOG.exception(exc)
raise exception.ComputeNodeNotFound(name=node.uuid)
else:
del self._nodes[node.uuid]
@lockutils.synchronized("model_root")
def add_instance(self, instance):
self.assert_instance(instance)
self._instances[instance.uuid] = instance
try:
super(ModelRoot, self).add_node(instance)
except nx.NetworkXError as exc:
LOG.exception(exc)
raise exception.InstanceNotFound(name=instance.uuid)
@lockutils.synchronized("model_root")
def remove_instance(self, instance):
self.assert_instance(instance)
del self._instances[instance.uuid]
super(ModelRoot, self).remove_node(instance)
@lockutils.synchronized("model_root")
def map_instance(self, instance, node):
"""Map a newly created instance to a node
@ -82,38 +95,25 @@ class ModelRoot(base.Model):
instance = self.get_instance_by_uuid(instance)
if isinstance(node, six.string_types):
node = self.get_node_by_uuid(node)
self.assert_node(node)
self.assert_instance(instance)
self.add_instance(instance)
self.mapping.map(node, instance)
self.add_edge(instance, node)
@lockutils.synchronized("model_root")
def unmap_instance(self, instance, node):
"""Unmap an instance from a node
:param instance: :py:class:`~.Instance` object or instance UUID
:type instance: str or :py:class:`~.Instance`
:param node: :py:class:`~.ComputeNode` object or node UUID
:type node: str or :py:class:`~.Instance`
"""
if isinstance(instance, six.string_types):
instance = self.get_instance_by_uuid(instance)
if isinstance(node, six.string_types):
node = self.get_node_by_uuid(node)
self.add_instance(instance)
self.mapping.unmap(node, instance)
self.remove_edge(instance, node)
def delete_instance(self, instance, node=None):
if node is not None:
self.mapping.unmap(node, instance)
self.assert_instance(instance)
self.remove_instance(instance)
for resource in self.resource.values():
try:
resource.unset_capacity(instance)
except KeyError:
pass
@lockutils.synchronized("model_root")
def migrate_instance(self, instance, source_node, destination_node):
"""Migrate single instance from source_node to destination_node
@ -122,96 +122,96 @@ class ModelRoot(base.Model):
:param destination_node:
:return:
"""
self.assert_instance(instance)
self.assert_node(source_node)
self.assert_node(destination_node)
if source_node == destination_node:
return False
# unmap
self.mapping.unmap(source_node, instance)
self.remove_edge(instance, source_node)
# map
self.mapping.map(destination_node, instance)
self.add_edge(instance, destination_node)
return True
@lockutils.synchronized("model_root")
def get_all_compute_nodes(self):
return self._nodes
return {cn.uuid: cn for cn in self.nodes()
if isinstance(cn, element.ComputeNode)}
def get_node_by_uuid(self, node_uuid):
if str(node_uuid) not in self._nodes:
raise exception.ComputeNodeNotFound(name=node_uuid)
return self._nodes[str(node_uuid)]
@lockutils.synchronized("model_root")
def get_node_by_uuid(self, uuid):
for graph_node in self.nodes():
if (isinstance(graph_node, element.ComputeNode) and
graph_node.uuid == uuid):
return graph_node
raise exception.ComputeNodeNotFound(name=uuid)
@lockutils.synchronized("model_root")
def get_instance_by_uuid(self, uuid):
if str(uuid) not in self._instances:
raise exception.InstanceNotFound(name=uuid)
return self._instances[str(uuid)]
return self._get_instance_by_uuid(uuid)
def _get_instance_by_uuid(self, uuid):
for graph_node in self.nodes():
if (isinstance(graph_node, element.Instance) and
graph_node.uuid == str(uuid)):
return graph_node
raise exception.InstanceNotFound(name=uuid)
@lockutils.synchronized("model_root")
def get_node_by_instance_uuid(self, instance_uuid):
"""Getting host information from the guest instance
:param instance_uuid: the uuid of the instance
:return: node
"""
if str(instance_uuid) not in self.mapping.instance_mapping:
raise exception.InstanceNotFound(name=instance_uuid)
return self.get_node_by_uuid(
self.mapping.instance_mapping[str(instance_uuid)])
instance = self._get_instance_by_uuid(instance_uuid)
for node in self.neighbors(instance):
if isinstance(node, element.ComputeNode):
return node
raise exception.ComputeNodeNotFound(name=instance_uuid)
@lockutils.synchronized("model_root")
def get_all_instances(self):
return self._instances
def get_mapping(self):
return self.mapping
def create_resource(self, r):
self.resource[str(r.name)] = r
return {inst.uuid: inst for inst in self.nodes()
if isinstance(inst, element.Instance)}
@lockutils.synchronized("model_root")
def get_resource_by_uuid(self, resource_id):
return self.resource[str(resource_id)]
# TODO(v-francoise): deprecate this method
# This is a trick to keep the compatibility with the old model root
class Resource(object):
def __init__(self, resource_id):
if isinstance(resource_id, element.ResourceType):
resource_id = resource_id.value
self.resource_id = resource_id
def get_capacity(self, element):
# We ignore element because value already contains the value
return getattr(element, self.resource_id)
return Resource(resource_id)
@lockutils.synchronized("model_root")
def get_node_instances(self, node):
return self.mapping.get_node_instances(node)
self.assert_node(node)
node_instances = []
for neighbor in self.predecessors(node):
if isinstance(neighbor, element.Instance):
node_instances.append(neighbor)
def _build_compute_node_element(self, compute_node):
attrib = collections.OrderedDict(
id=six.text_type(compute_node.id), uuid=compute_node.uuid,
human_id=compute_node.human_id, hostname=compute_node.hostname,
state=compute_node.state, status=compute_node.status)
for resource_name, resource in sorted(
self.resource.items(), key=lambda x: x[0]):
res_value = resource.get_capacity(compute_node)
if res_value is not None:
attrib[resource_name] = six.text_type(res_value)
compute_node_el = etree.Element("ComputeNode", attrib=attrib)
return compute_node_el
def _build_instance_element(self, instance):
attrib = collections.OrderedDict(
uuid=instance.uuid, human_id=instance.human_id,
hostname=instance.hostname, state=instance.state)
for resource_name, resource in sorted(
self.resource.items(), key=lambda x: x[0]):
res_value = resource.get_capacity(instance)
if res_value is not None:
attrib[resource_name] = six.text_type(res_value)
instance_el = etree.Element("Instance", attrib=attrib)
return instance_el
return node_instances
def to_string(self):
return self.to_xml()
def to_xml(self):
root = etree.Element("ModelRoot")
# Build compute node tree
for cn in sorted(self.get_all_compute_nodes().values(),
key=lambda cn: cn.uuid):
compute_node_el = self._build_compute_node_element(cn)
compute_node_el = cn.as_xml_element()
# Build mapped instance tree
node_instance_uuids = self.get_node_instances(cn)
for instance_uuid in sorted(node_instance_uuids):
instance = self.get_instance_by_uuid(instance_uuid)
instance_el = self._build_instance_element(instance)
node_instances = self.get_node_instances(cn)
for instance in sorted(node_instances, key=lambda x: x.uuid):
instance_el = instance.as_xml_element()
compute_node_el.append(instance_el)
root.append(compute_node_el)
@ -221,51 +221,23 @@ class ModelRoot(base.Model):
key=lambda inst: inst.uuid):
try:
self.get_node_by_instance_uuid(instance.uuid)
except exception.InstanceNotFound:
root.append(self._build_instance_element(instance))
except (exception.InstanceNotFound, exception.ComputeNodeNotFound):
root.append(instance.as_xml_element())
return etree.tostring(root, pretty_print=True).decode('utf-8')
@classmethod
def from_xml(cls, data):
model = cls()
root = etree.fromstring(data)
mem = element.Resource(element.ResourceType.memory)
num_cores = element.Resource(element.ResourceType.cpu_cores)
disk = element.Resource(element.ResourceType.disk)
disk_capacity = element.Resource(element.ResourceType.disk_capacity)
model.create_resource(mem)
model.create_resource(num_cores)
model.create_resource(disk)
model.create_resource(disk_capacity)
for cn in root.findall('.//ComputeNode'):
node = element.ComputeNode(cn.get('id'))
node.uuid = cn.get('uuid')
node.hostname = cn.get('hostname')
# set capacity
mem.set_capacity(node, int(cn.get(str(mem.name))))
disk.set_capacity(node, int(cn.get(str(disk.name))))
disk_capacity.set_capacity(
node, int(cn.get(str(disk_capacity.name))))
num_cores.set_capacity(node, int(cn.get(str(num_cores.name))))
node.state = cn.get('state')
node.status = cn.get('status')
node = element.ComputeNode(**cn.attrib)
model.add_node(node)
for inst in root.findall('.//Instance'):
instance = element.Instance()
instance.uuid = inst.get('uuid')
instance.state = inst.get('state')
mem.set_capacity(instance, int(inst.get(str(mem.name))))
disk.set_capacity(instance, int(inst.get(str(disk.name))))
disk_capacity.set_capacity(
instance, int(inst.get(str(disk_capacity.name))))
num_cores.set_capacity(
instance, int(inst.get(str(num_cores.name))))
instance = element.Instance(**inst.attrib)
model.add_instance(instance)
parent = inst.getparent()
if parent.tag == 'ComputeNode':

View File

@ -18,7 +18,7 @@
from oslo_log import log
from watcher._i18n import _LI
from watcher._i18n import _LI, _LW
from watcher.common import exception
from watcher.common import nova_helper
from watcher.decision_engine.model import element
@ -40,14 +40,21 @@ class NovaNotification(base.NotificationEndpoint):
self._nova = nova_helper.NovaHelper()
return self._nova
def get_or_create_instance(self, uuid):
def get_or_create_instance(self, instance_uuid, node_uuid=None):
try:
instance = self.cluster_data_model.get_instance_by_uuid(uuid)
if node_uuid:
self.get_or_create_node(node_uuid)
except exception.ComputeNodeNotFound:
LOG.warning(_LW("Could not find compute node %(node)s for "
"instance %(instance)s"),
dict(node=node_uuid, instance=instance_uuid))
try:
instance = self.cluster_data_model.get_instance_by_uuid(
instance_uuid)
except exception.InstanceNotFound:
# The instance didn't exist yet so we create a new instance object
LOG.debug("New instance created: %s", uuid)
instance = element.Instance()
instance.uuid = uuid
LOG.debug("New instance created: %s", instance_uuid)
instance = element.Instance(uuid=instance_uuid)
self.cluster_data_model.add_instance(instance)
@ -57,9 +64,11 @@ class NovaNotification(base.NotificationEndpoint):
instance_data = data['nova_object.data']
instance_flavor_data = instance_data['flavor']['nova_object.data']
instance.state = instance_data['state']
instance.hostname = instance_data['host_name']
instance.human_id = instance_data['display_name']
instance.update({
'state': instance_data['state'],
'hostname': instance_data['host_name'],
'human_id': instance_data['display_name'],
})
memory_mb = instance_flavor_data['memory_mb']
num_cores = instance_flavor_data['vcpus']
@ -67,7 +76,7 @@ class NovaNotification(base.NotificationEndpoint):
self.update_capacity(element.ResourceType.memory, instance, memory_mb)
self.update_capacity(
element.ResourceType.cpu_cores, instance, num_cores)
element.ResourceType.vcpus, instance, num_cores)
self.update_capacity(
element.ResourceType.disk, instance, disk_gb)
self.update_capacity(
@ -83,13 +92,14 @@ class NovaNotification(base.NotificationEndpoint):
self.update_instance_mapping(instance, node)
def update_capacity(self, resource_id, obj, value):
resource = self.cluster_data_model.get_resource_by_uuid(resource_id)
resource.set_capacity(obj, value)
setattr(obj, resource_id.value, value)
def legacy_update_instance(self, instance, data):
instance.state = data['state']
instance.hostname = data['hostname']
instance.human_id = data['display_name']
instance.update({
'state': data['state'],
'hostname': data['hostname'],
'human_id': data['display_name'],
})
memory_mb = data['memory_mb']
num_cores = data['vcpus']
@ -97,7 +107,7 @@ class NovaNotification(base.NotificationEndpoint):
self.update_capacity(element.ResourceType.memory, instance, memory_mb)
self.update_capacity(
element.ResourceType.cpu_cores, instance, num_cores)
element.ResourceType.vcpus, instance, num_cores)
self.update_capacity(
element.ResourceType.disk, instance, disk_gb)
self.update_capacity(
@ -115,28 +125,34 @@ class NovaNotification(base.NotificationEndpoint):
def update_compute_node(self, node, data):
"""Update the compute node using the notification data."""
node_data = data['nova_object.data']
node.hostname = node_data['host']
node.state = (
node_state = (
element.ServiceState.OFFLINE.value
if node_data['forced_down'] else element.ServiceState.ONLINE.value)
node.status = (
node_status = (
element.ServiceState.DISABLED.value
if node_data['disabled'] else element.ServiceState.ENABLED.value)
node.update({
'hostname': node_data['host'],
'state': node_state,
'status': node_status,
})
def create_compute_node(self, node_hostname):
"""Update the compute node by querying the Nova API."""
try:
_node = self.nova.get_compute_node_by_hostname(node_hostname)
node = element.ComputeNode(_node.id)
node.uuid = node_hostname
node.hostname = _node.hypervisor_hostname
node.state = _node.state
node.status = _node.status
node = element.ComputeNode(
id=_node.id,
uuid=node_hostname,
hostname=_node.hypervisor_hostname,
state=_node.state,
status=_node.status)
self.update_capacity(
element.ResourceType.memory, node, _node.memory_mb)
self.update_capacity(
element.ResourceType.cpu_cores, node, _node.vcpus)
element.ResourceType.vcpus, node, _node.vcpus)
self.update_capacity(
element.ResourceType.disk, node, _node.free_disk_gb)
self.update_capacity(
@ -170,18 +186,20 @@ class NovaNotification(base.NotificationEndpoint):
return
try:
try:
old_node = self.get_or_create_node(node.uuid)
current_node = (
self.cluster_data_model.get_node_by_instance_uuid(
instance.uuid) or self.get_or_create_node(node.uuid))
except exception.ComputeNodeNotFound as exc:
LOG.exception(exc)
# If we can't create the node,
# we consider the instance as unmapped
old_node = None
current_node = None
LOG.debug("Mapped node %s found", node.uuid)
if node and node != old_node:
if current_node and node != current_node:
LOG.debug("Unmapping instance %s from %s",
instance.uuid, node.uuid)
self.cluster_data_model.unmap_instance(instance, old_node)
self.cluster_data_model.unmap_instance(instance, current_node)
except exception.InstanceNotFound:
# The instance didn't exist yet so we map it for the first time
LOG.debug("New instance: mapping it to %s", node.uuid)
@ -221,6 +239,7 @@ class ServiceUpdated(VersionnedNotificationEndpoint):
dict(event=event_type,
publisher=publisher_id,
metadata=metadata))
LOG.debug(payload)
node_data = payload['nova_object.data']
node_uuid = node_data['host']
try:
@ -262,10 +281,12 @@ class InstanceCreated(VersionnedNotificationEndpoint):
dict(event=event_type,
publisher=publisher_id,
metadata=metadata))
LOG.debug(payload)
instance_data = payload['nova_object.data']
instance_uuid = instance_data['uuid']
instance = self.get_or_create_instance(instance_uuid)
node_uuid = instance_data.get('host')
instance = self.get_or_create_instance(instance_uuid, node_uuid)
self.update_instance(instance, payload)
@ -294,9 +315,11 @@ class InstanceUpdated(VersionnedNotificationEndpoint):
dict(event=event_type,
publisher=publisher_id,
metadata=metadata))
LOG.debug(payload)
instance_data = payload['nova_object.data']
instance_uuid = instance_data['uuid']
instance = self.get_or_create_instance(instance_uuid)
node_uuid = instance_data.get('host')
instance = self.get_or_create_instance(instance_uuid, node_uuid)
self.update_instance(instance, payload)
@ -317,10 +340,12 @@ class InstanceDeletedEnd(VersionnedNotificationEndpoint):
dict(event=event_type,
publisher=publisher_id,
metadata=metadata))
LOG.debug(payload)
instance_data = payload['nova_object.data']
instance_uuid = instance_data['uuid']
instance = self.get_or_create_instance(instance_uuid)
node_uuid = instance_data.get('host')
instance = self.get_or_create_instance(instance_uuid, node_uuid)
try:
node = self.get_or_create_node(instance_data['host'])
@ -348,9 +373,11 @@ class LegacyInstanceUpdated(UnversionnedNotificationEndpoint):
dict(event=event_type,
publisher=publisher_id,
metadata=metadata))
LOG.debug(payload)
instance_uuid = payload['instance_id']
instance = self.get_or_create_instance(instance_uuid)
node_uuid = payload.get('node')
instance = self.get_or_create_instance(instance_uuid, node_uuid)
self.legacy_update_instance(instance, payload)
@ -371,9 +398,11 @@ class LegacyInstanceCreatedEnd(UnversionnedNotificationEndpoint):
dict(event=event_type,
publisher=publisher_id,
metadata=metadata))
LOG.debug(payload)
instance_uuid = payload['instance_id']
instance = self.get_or_create_instance(instance_uuid)
node_uuid = payload.get('node')
instance = self.get_or_create_instance(instance_uuid, node_uuid)
self.legacy_update_instance(instance, payload)
@ -394,8 +423,10 @@ class LegacyInstanceDeletedEnd(UnversionnedNotificationEndpoint):
dict(event=event_type,
publisher=publisher_id,
metadata=metadata))
LOG.debug(payload)
instance_uuid = payload['instance_id']
instance = self.get_or_create_instance(instance_uuid)
node_uuid = payload.get('node')
instance = self.get_or_create_instance(instance_uuid, node_uuid)
try:
node = self.get_or_create_node(payload['host'])
@ -423,8 +454,10 @@ class LegacyLiveMigratedEnd(UnversionnedNotificationEndpoint):
dict(event=event_type,
publisher=publisher_id,
metadata=metadata))
LOG.debug(payload)
instance_uuid = payload['instance_id']
instance = self.get_or_create_instance(instance_uuid)
node_uuid = payload.get('node')
instance = self.get_or_create_instance(instance_uuid, node_uuid)
self.legacy_update_instance(instance, payload)

View File

@ -13,9 +13,6 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import copy
from oslo_log import log
@ -101,9 +98,8 @@ class DefaultScope(base.BaseScope):
self._osc = osc
self.wrapper = nova_helper.NovaHelper(osc=self._osc)
def _remove_instance(self, cluster_model, instance_uuid, node_name):
def remove_instance(self, cluster_model, instance, node_name):
node = cluster_model.get_node_by_uuid(node_name)
instance = cluster_model.get_instance_by_uuid(instance_uuid)
cluster_model.delete_instance(instance, node)
def _check_wildcard(self, aggregate_list):
@ -147,7 +143,7 @@ class DefaultScope(base.BaseScope):
if zone.zoneName in zone_names or include_all_nodes:
allowed_nodes.extend(zone.hosts.keys())
def _exclude_resources(self, resources, **kwargs):
def exclude_resources(self, resources, **kwargs):
instances_to_exclude = kwargs.get('instances')
nodes_to_exclude = kwargs.get('nodes')
for resource in resources:
@ -160,32 +156,32 @@ class DefaultScope(base.BaseScope):
[host['name'] for host
in resource['compute_nodes']])
def _remove_node_from_model(self, nodes_to_remove, cluster_model):
for node_name in nodes_to_remove:
instances = copy.copy(
cluster_model.get_mapping().get_node_instances_by_uuid(
node_name))
for instance_uuid in instances:
self._remove_instance(cluster_model, instance_uuid, node_name)
node = cluster_model.get_node_by_uuid(node_name)
def remove_nodes_from_model(self, nodes_to_remove, cluster_model):
for node_uuid in nodes_to_remove:
node = cluster_model.get_node_by_uuid(node_uuid)
instances = cluster_model.get_node_instances(node)
for instance in instances:
self.remove_instance(cluster_model, instance, node_uuid)
cluster_model.remove_node(node)
def _remove_instances_from_model(self, instances_to_remove, cluster_model):
def remove_instances_from_model(self, instances_to_remove, cluster_model):
for instance_uuid in instances_to_remove:
try:
node_name = (cluster_model.get_mapping()
.get_node_by_instance_uuid(instance_uuid).uuid)
except KeyError:
node_name = cluster_model.get_node_by_instance_uuid(
instance_uuid).uuid
except exception.InstanceNotFound:
LOG.warning(_LW("The following instance %s cannot be found. "
"It might be deleted from CDM along with node"
" instance was hosted on."),
instance_uuid)
continue
self._remove_instance(cluster_model, instance_uuid, node_name)
self.remove_instance(
cluster_model,
cluster_model.get_instance_by_uuid(instance_uuid),
node_name)
def get_scoped_model(self, cluster_model):
"""Leave only nodes and instances proposed in the audit scope"""
if not cluster_model:
return None
@ -206,7 +202,7 @@ class DefaultScope(base.BaseScope):
self._collect_zones(rule['availability_zones'],
allowed_nodes)
elif 'exclude' in rule:
self._exclude_resources(
self.exclude_resources(
rule['exclude'], instances=instances_to_exclude,
nodes=nodes_to_exclude)
@ -215,7 +211,7 @@ class DefaultScope(base.BaseScope):
nodes_to_remove = set(model_hosts) - set(allowed_nodes)
nodes_to_remove.update(nodes_to_exclude)
self._remove_node_from_model(nodes_to_remove, cluster_model)
self._remove_instances_from_model(instances_to_remove, cluster_model)
self.remove_nodes_from_model(nodes_to_remove, cluster_model)
self.remove_instances_from_model(instances_to_remove, cluster_model)
return cluster_model

View File

@ -174,15 +174,14 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
total_disk = 0
total_mem = 0
cpu_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores)
element.ResourceType.vcpus)
disk_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.disk)
memory_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.memory)
for instance_id in self.compute_model.mapping.get_node_instances(
for instance in self.compute_model.get_node_instances(
destination_node):
instance = self.compute_model.get_instance_by_uuid(instance_id)
total_cores += cpu_capacity.get_capacity(instance)
total_disk += disk_capacity.get_capacity(instance)
total_mem += memory_capacity.get_capacity(instance)
@ -210,7 +209,7 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
:return: True if the threshold is not exceed
"""
cpu_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores).get_capacity(destination_node)
element.ResourceType.vcpus).get_capacity(destination_node)
disk_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.disk).get_capacity(destination_node)
memory_capacity = self.compute_model.get_resource_by_uuid(
@ -231,7 +230,7 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
:return:
"""
cpu_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores).get_capacity(compute_resource)
element.ResourceType.vcpus).get_capacity(compute_resource)
disk_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.disk).get_capacity(compute_resource)
@ -333,7 +332,7 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
host_avg_cpu_util = 100
cpu_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores).get_capacity(node)
element.ResourceType.vcpus).get_capacity(node)
total_cores_used = cpu_capacity * (host_avg_cpu_util / 100.0)
@ -356,7 +355,7 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
instance_cpu_utilization = 100
cpu_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores).get_capacity(instance)
element.ResourceType.vcpus).get_capacity(instance)
total_cores_used = cpu_capacity * (instance_cpu_utilization / 100.0)
@ -387,28 +386,24 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
if node.status == element.ServiceState.ENABLED.value:
self.number_of_enabled_nodes += 1
count = self.compute_model.mapping.get_node_instances(node)
if len(count) > 0:
instances = self.compute_model.get_node_instances(node)
if len(instances) > 0:
result = self.calculate_score_node(node)
else:
# The node has not VMs
result = 0
if len(count) > 0:
score.append((node.uuid, result))
return score
def node_and_instance_score(self, sorted_scores):
"""Get List of VMs from node"""
node_to_release = sorted_scores[len(sorted_scores) - 1][0]
instances_to_migrate = self.compute_model.mapping.get_node_instances(
instances_to_migrate = self.compute_model.get_node_instances(
self.compute_model.get_node_by_uuid(node_to_release))
instance_score = []
for instance_id in instances_to_migrate:
instance = self.compute_model.get_instance_by_uuid(instance_id)
for instance in instances_to_migrate:
if instance.state == element.InstanceState.ACTIVE.value:
instance_score.append(
(instance_id, self.calculate_score_instance(instance)))
(instance, self.calculate_score_instance(instance)))
return node_to_release, instance_score
@ -421,8 +416,7 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
mig_source_node.uuid,
mig_destination_node.uuid)
if len(self.compute_model.mapping.get_node_instances(
mig_source_node)) == 0:
if len(self.compute_model.get_node_instances(mig_source_node)) == 0:
self.add_change_service_state(mig_source_node.
uuid,
element.ServiceState.DISABLED.value)
@ -431,10 +425,8 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
def calculate_num_migrations(self, sorted_instances, node_to_release,
sorted_score):
number_migrations = 0
for instance in sorted_instances:
for mig_instance, __ in sorted_instances:
for j in range(0, len(sorted_score)):
mig_instance = self.compute_model.get_instance_by_uuid(
instance[0])
mig_source_node = self.compute_model.get_node_by_uuid(
node_to_release)
mig_destination_node = self.compute_model.get_node_by_uuid(

View File

@ -124,16 +124,14 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
def calc_used_res(self, node, cpu_capacity,
memory_capacity, disk_capacity):
"""Calculate the used vcpus, memory and disk based on VM flavors"""
instances = self.compute_model.mapping.get_node_instances(node)
instances = self.compute_model.get_node_instances(node)
vcpus_used = 0
memory_mb_used = 0
disk_gb_used = 0
if len(instances) > 0:
for instance_id in instances:
instance = self.compute_model.get_instance_by_uuid(instance_id)
vcpus_used += cpu_capacity.get_capacity(instance)
memory_mb_used += memory_capacity.get_capacity(instance)
disk_gb_used += disk_capacity.get_capacity(instance)
for instance in instances:
vcpus_used += cpu_capacity.get_capacity(instance)
memory_mb_used += memory_capacity.get_capacity(instance)
disk_gb_used += disk_capacity.get_capacity(instance)
return vcpus_used, memory_mb_used, disk_gb_used
@ -146,9 +144,7 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
hosts_need_release = []
hosts_target = []
for node_id in nodes:
node = self.compute_model.get_node_by_uuid(
node_id)
for node in nodes.values():
resource_id = node.uuid
outlet_temp = self.ceilometer.statistic_aggregation(
@ -174,30 +170,27 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
"""Pick up an active instance to migrate from provided hosts"""
for instance_data in hosts:
mig_source_node = instance_data['node']
instances_of_src = self.compute_model.mapping.get_node_instances(
instances_of_src = self.compute_model.get_node_instances(
mig_source_node)
if len(instances_of_src) > 0:
for instance_id in instances_of_src:
try:
# select the first active instance to migrate
instance = self.compute_model.get_instance_by_uuid(
instance_id)
if (instance.state !=
element.InstanceState.ACTIVE.value):
LOG.info(_LI("Instance not active, skipped: %s"),
instance.uuid)
continue
return mig_source_node, instance
except wexc.InstanceNotFound as e:
LOG.exception(e)
LOG.info(_LI("Instance not found"))
for instance in instances_of_src:
try:
# select the first active instance to migrate
if (instance.state !=
element.InstanceState.ACTIVE.value):
LOG.info(_LI("Instance not active, skipped: %s"),
instance.uuid)
continue
return mig_source_node, instance
except wexc.InstanceNotFound as e:
LOG.exception(e)
LOG.info(_LI("Instance not found"))
return None
def filter_dest_servers(self, hosts, instance_to_migrate):
"""Only return hosts with sufficient available resources"""
cpu_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores)
element.ResourceType.vcpus)
disk_capacity = self.compute_model.get_resource_by_uuid(
element.ResourceType.disk)
memory_capacity = self.compute_model.get_resource_by_uuid(

View File

@ -45,7 +45,7 @@ airflow is higher than the specified threshold.
from oslo_log import log
from watcher._i18n import _, _LE, _LI, _LW
from watcher._i18n import _, _LI, _LW
from watcher.common import exception as wexc
from watcher.datasource import ceilometer as ceil
from watcher.decision_engine.model import element
@ -166,13 +166,11 @@ class UniformAirflow(base.BaseStrategy):
def calculate_used_resource(self, node, cap_cores, cap_mem, cap_disk):
"""Compute the used vcpus, memory and disk based on instance flavors"""
instances = self.compute_model.mapping.get_node_instances(node)
instances = self.compute_model.get_node_instances(node)
vcpus_used = 0
memory_mb_used = 0
disk_gb_used = 0
for instance_id in instances:
instance = self.compute_model.get_instance_by_uuid(
instance_id)
for instance in instances:
vcpus_used += cap_cores.get_capacity(instance)
memory_mb_used += cap_mem.get_capacity(instance)
disk_gb_used += cap_disk.get_capacity(instance)
@ -187,7 +185,7 @@ class UniformAirflow(base.BaseStrategy):
instances_tobe_migrate = []
for nodemap in hosts:
source_node = nodemap['node']
source_instances = self.compute_model.mapping.get_node_instances(
source_instances = self.compute_model.get_node_instances(
source_node)
if source_instances:
inlet_t = self.ceilometer.statistic_aggregation(
@ -203,32 +201,20 @@ class UniformAirflow(base.BaseStrategy):
if (power < self.threshold_power and
inlet_t < self.threshold_inlet_t):
# hardware issue, migrate all instances from this node
for instance_id in source_instances:
try:
instance = (self.compute_model.
get_instance_by_uuid(instance_id))
instances_tobe_migrate.append(instance)
except wexc.InstanceNotFound:
LOG.error(_LE("Instance not found; error: %s"),
instance_id)
for instance in source_instances:
instances_tobe_migrate.append(instance)
return source_node, instances_tobe_migrate
else:
# migrate the first active instance
for instance_id in source_instances:
try:
instance = (self.compute_model.
get_instance_by_uuid(instance_id))
if (instance.state !=
element.InstanceState.ACTIVE.value):
LOG.info(
_LI("Instance not active, skipped: %s"),
instance.uuid)
continue
instances_tobe_migrate.append(instance)
return source_node, instances_tobe_migrate
except wexc.InstanceNotFound:
LOG.error(_LE("Instance not found; error: %s"),
instance_id)
for instance in source_instances:
if (instance.state !=
element.InstanceState.ACTIVE.value):
LOG.info(
_LI("Instance not active, skipped: %s"),
instance.uuid)
continue
instances_tobe_migrate.append(instance)
return source_node, instances_tobe_migrate
else:
LOG.info(_LI("Instance not found on node: %s"),
source_node.uuid)

View File

@ -139,18 +139,16 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
input_parameters=params)
self.number_of_released_nodes += 1
def add_migration(self, instance_uuid, source_node,
def add_migration(self, instance, source_node,
destination_node, model):
"""Add an action for VM migration into the solution.
:param instance_uuid: instance uuid
:param instance: instance object
:param source_node: node object
:param destination_node: node object
:param model: model_root object
:return: None
"""
instance = model.get_instance_by_uuid(instance_uuid)
instance_state_str = self.get_state_str(instance.state)
if instance_state_str != element.InstanceState.ACTIVE.value:
# Watcher curently only supports live VM migration and block live
@ -160,7 +158,7 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
LOG.error(
_LE('Cannot live migrate: instance_uuid=%(instance_uuid)s, '
'state=%(instance_state)s.') % dict(
instance_uuid=instance_uuid,
instance_uuid=instance.uuid,
instance_state=instance_state_str))
return
@ -169,6 +167,7 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
destination_node_state_str = self.get_state_str(destination_node.state)
if destination_node_state_str == element.ServiceState.DISABLED.value:
self.add_action_enable_compute_node(destination_node)
if model.migrate_instance(instance, source_node, destination_node):
params = {'migration_type': migration_type,
'source_node': source_node.uuid,
@ -185,7 +184,7 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
:return: None
"""
for node in model.get_all_compute_nodes().values():
if (len(model.mapping.get_node_instances(node)) == 0 and
if (len(model.get_node_instances(node)) == 0 and
node.status !=
element.ServiceState.DISABLED.value):
self.add_action_disable_node(node)
@ -254,14 +253,13 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
:param aggr: string
:return: dict(cpu(number of cores used), ram(MB used), disk(B used))
"""
node_instances = model.mapping.get_node_instances_by_uuid(
node.uuid)
node_instances = model.get_node_instances(node)
node_ram_util = 0
node_disk_util = 0
node_cpu_util = 0
for instance_uuid in node_instances:
for instance in node_instances:
instance_util = self.get_instance_utilization(
instance_uuid, model, period, aggr)
instance.uuid, model, period, aggr)
node_cpu_util += instance_util['cpu']
node_ram_util += instance_util['ram']
node_disk_util += instance_util['disk']
@ -402,9 +400,9 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
self.number_of_migrations -= 1
src_node = model.get_node_by_uuid(src_uuid)
dst_node = model.get_node_by_uuid(dst_uuid)
if model.migrate_instance(instance_uuid, dst_node, src_node):
self.add_migration(
instance_uuid, src_node, dst_node, model)
instance = model.get_instance_by_uuid(instance_uuid)
if model.migrate_instance(instance, dst_node, src_node):
self.add_migration(instance, src_node, dst_node, model)
def offload_phase(self, model, cc):
"""Perform offloading phase.
@ -431,13 +429,13 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
for node in reversed(sorted_nodes):
if self.is_overloaded(node, model, cc):
for instance in sorted(
model.mapping.get_node_instances(node),
model.get_node_instances(node),
key=lambda x: self.get_instance_utilization(
x, model)['cpu']
x.uuid, model)['cpu']
):
for destination_node in reversed(sorted_nodes):
if self.instance_fits(
instance, destination_node, model, cc):
instance.uuid, destination_node, model, cc):
self.add_migration(instance, node,
destination_node, model)
break
@ -465,15 +463,16 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
asc = 0
for node in sorted_nodes:
instances = sorted(
model.mapping.get_node_instances(node),
key=lambda x: self.get_instance_utilization(x, model)['cpu'])
model.get_node_instances(node),
key=lambda x: self.get_instance_utilization(
x.uuid, model)['cpu'])
for instance in reversed(instances):
dsc = len(sorted_nodes) - 1
for destination_node in reversed(sorted_nodes):
if asc >= dsc:
break
if self.instance_fits(
instance, destination_node, model, cc):
instance.uuid, destination_node, model, cc):
self.add_migration(instance, node,
destination_node, model)
break

View File

@ -148,12 +148,11 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
def calculate_used_resource(self, node, cap_cores, cap_mem,
cap_disk):
"""Calculate the used vcpus, memory and disk based on VM flavors"""
instances = self.compute_model.mapping.get_node_instances(node)
instances = self.compute_model.get_node_instances(node)
vcpus_used = 0
memory_mb_used = 0
disk_gb_used = 0
for instance_id in instances:
instance = self.compute_model.get_instance_by_uuid(instance_id)
for instance in instances:
vcpus_used += cap_cores.get_capacity(instance)
memory_mb_used += cap_mem.get_capacity(instance)
disk_gb_used += cap_disk.get_capacity(instance)
@ -169,27 +168,25 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
"""
for instance_data in hosts:
source_node = instance_data['node']
source_instances = self.compute_model.mapping.get_node_instances(
source_instances = self.compute_model.get_node_instances(
source_node)
if source_instances:
delta_workload = instance_data['workload'] - avg_workload
min_delta = 1000000
instance_id = None
for inst_id in source_instances:
for instance in source_instances:
try:
# select the first active VM to migrate
instance = self.compute_model.get_instance_by_uuid(
inst_id)
if (instance.state !=
element.InstanceState.ACTIVE.value):
LOG.debug("Instance not active, skipped: %s",
instance.uuid)
continue
current_delta = (
delta_workload - workload_cache[inst_id])
delta_workload - workload_cache[instance.uuid])
if 0 <= current_delta < min_delta:
min_delta = current_delta
instance_id = inst_id
instance_id = instance.uuid
except wexc.InstanceNotFound:
LOG.error(_LE("Instance not found; error: %s"),
instance_id)
@ -254,7 +251,7 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
raise wexc.ClusterEmpty()
# get cpu cores capacity of nodes and instances
cap_cores = self.compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores)
element.ResourceType.vcpus)
overload_hosts = []
nonoverload_hosts = []
# total workload of cluster
@ -264,13 +261,12 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
for node_id in nodes:
node = self.compute_model.get_node_by_uuid(
node_id)
instances = self.compute_model.mapping.get_node_instances(node)
instances = self.compute_model.get_node_instances(node)
node_workload = 0.0
for instance_id in instances:
instance = self.compute_model.get_instance_by_uuid(instance_id)
for instance in instances:
try:
cpu_util = self.ceilometer.statistic_aggregation(
resource_id=instance_id,
resource_id=instance.uuid,
meter_name=self._meter,
period=self._period,
aggregate='avg')
@ -279,12 +275,12 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
LOG.error(_LE("Can not get cpu_util from Ceilometer"))
continue
if cpu_util is None:
LOG.debug("Instance (%s): cpu_util is None", instance_id)
LOG.debug("Instance (%s): cpu_util is None", instance.uuid)
continue
instance_cores = cap_cores.get_capacity(instance)
workload_cache[instance_id] = cpu_util * instance_cores / 100
node_workload += workload_cache[instance_id]
LOG.debug("VM (%s): cpu_util %f", instance_id, cpu_util)
workload_cache[instance.uuid] = cpu_util * instance_cores / 100
node_workload += workload_cache[instance.uuid]
LOG.debug("VM (%s): cpu_util %f", instance.uuid, cpu_util)
node_cores = cap_cores.get_capacity(node)
hy_cpu_util = node_workload / node_cores * 100

View File

@ -343,29 +343,26 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
instance_host_map = []
nodes = list(self.get_available_nodes())
for source_hp_id in nodes:
for src_node_id in nodes:
src_node = self.compute_model.get_node_by_uuid(src_node_id)
c_nodes = copy.copy(nodes)
c_nodes.remove(source_hp_id)
c_nodes.remove(src_node_id)
node_list = yield_nodes(c_nodes)
instances_id = self.compute_model.get_mapping(). \
get_node_instances_by_uuid(source_hp_id)
for instance_id in instances_id:
for instance in self.compute_model.get_node_instances(src_node):
min_sd_case = {'value': len(self.metrics)}
instance = self.compute_model.get_instance_by_uuid(instance_id)
if instance.state not in [element.InstanceState.ACTIVE.value,
element.InstanceState.PAUSED.value]:
continue
for dst_node_id in next(node_list):
sd_case = self.calculate_migration_case(hosts, instance_id,
source_hp_id,
dst_node_id)
sd_case = self.calculate_migration_case(
hosts, instance.uuid, src_node_id, dst_node_id)
weighted_sd = self.calculate_weighted_sd(sd_case[:-1])
if weighted_sd < min_sd_case['value']:
min_sd_case = {
'host': dst_node_id, 'value': weighted_sd,
's_host': source_hp_id, 'instance': instance_id}
's_host': src_node_id, 'instance': instance.uuid}
instance_host_map.append(min_sd_case)
return sorted(instance_host_map, key=lambda x: x['value'])

View File

@ -24,10 +24,14 @@ BaseEnumField = fields.BaseEnumField
BooleanField = fields.BooleanField
DateTimeField = fields.DateTimeField
Enum = fields.Enum
FloatField = fields.FloatField
IntegerField = fields.IntegerField
ListOfStringsField = fields.ListOfStringsField
NonNegativeFloatField = fields.NonNegativeFloatField
NonNegativeIntegerField = fields.NonNegativeIntegerField
ObjectField = fields.ObjectField
StringField = fields.StringField
UnspecifiedDefault = fields.UnspecifiedDefault
UUIDField = fields.UUIDField

View File

@ -19,6 +19,7 @@
import mock
from watcher.common import nova_helper
from watcher.common import utils
from watcher.decision_engine.model.collector import nova
from watcher.tests import base
from watcher.tests import conf_fixture
@ -33,9 +34,13 @@ class TestNovaClusterDataModelCollector(base.TestCase):
@mock.patch('keystoneclient.v3.client.Client', mock.Mock())
@mock.patch.object(nova_helper, 'NovaHelper')
def test_nova_cdmc_execute(self, m_nova_helper_cls):
m_nova_helper = mock.Mock()
m_nova_helper = mock.Mock(name="nova_helper")
m_nova_helper_cls.return_value = m_nova_helper
m_nova_helper.get_service.return_value = mock.Mock(
host="test_hostname")
fake_compute_node = mock.Mock(
id=1337,
service={'id': 123},
hypervisor_hostname='test_hostname',
memory_mb=333,
@ -47,19 +52,16 @@ class TestNovaClusterDataModelCollector(base.TestCase):
)
fake_instance = mock.Mock(
id='ef500f7e-dac8-470f-960c-169486fce71b',
state=mock.Mock(**{'OS-EXT-STS:vm_state': 'VM_STATE'}),
flavor={'ram': 333, 'disk': 222, 'vcpus': 4},
human_id='fake_instance',
flavor={'ram': 333, 'disk': 222, 'vcpus': 4, 'id': 1},
)
setattr(fake_instance, 'OS-EXT-STS:vm_state', 'VM_STATE')
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.nova.services.find.return_value = mock.Mock(
host='test_hostname')
# m_nova_helper.get_instances_by_node.return_value = [fake_instance]
m_nova_helper.get_instance_list.return_value = [fake_instance]
def m_get_flavor_instance(instance, cache):
instance.flavor = {'ram': 333, 'disk': 222, 'vcpus': 4}
return instance
m_nova_helper.get_flavor_instance.side_effect = m_get_flavor_instance
m_nova_helper.get_flavor.return_value = utils.Struct(**{
'ram': 333, 'disk': 222, 'vcpus': 4})
m_config = mock.Mock()
m_osc = mock.Mock()

View File

@ -1,47 +1,47 @@
<ModelRoot>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_0" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_1" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_0" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_1" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode human_id="" uuid="Node_1" status="enabled" state="up" id="1" hostname="hostname_1" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_2" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_1" status="enabled" state="up" id="1" hostname="hostname_1" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_2" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode human_id="" uuid="Node_2" status="enabled" state="up" id="2" hostname="hostname_2" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_3" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_4" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_5" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_2" status="enabled" state="up" id="2" hostname="hostname_2" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_3" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_4" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_5" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode human_id="" uuid="Node_3" status="enabled" state="up" id="3" hostname="hostname_3" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_6" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_3" status="enabled" state="up" id="3" hostname="hostname_3" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_6" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode human_id="" uuid="Node_4" status="enabled" state="up" id="4" hostname="hostname_4" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_7" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_4" status="enabled" state="up" id="4" hostname="hostname_4" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_7" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<Instance state="active" human_id="" uuid="INSTANCE_10" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_11" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_12" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_13" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_14" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_15" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_16" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_17" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_18" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_19" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_20" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_21" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_22" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_23" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_24" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_25" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_26" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_27" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_28" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_29" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_30" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_31" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_32" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_33" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_34" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_8" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_9" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_10" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_11" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_12" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_13" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_14" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_15" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_16" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_17" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_18" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_19" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_20" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_21" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_22" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_23" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_24" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_25" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_26" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_27" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_28" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_29" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_30" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_31" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_32" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_33" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_34" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_8" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_9" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ModelRoot>

View File

@ -1,8 +1,8 @@
<ModelRoot>
<ComputeNode hostname="hostname_0" uuid="Node_0" id="0" state="enabled" human_id="" status="enabled" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_0" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_0" uuid="Node_0" id="0" state="enabled" human_id="" status="enabled" vcpus="40" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_0" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode hostname="hostname_1" uuid="Node_1" id="1" state="enabled" human_id="" status="enabled" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_1" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_1" uuid="Node_1" id="1" state="enabled" human_id="" status="enabled" vcpus="40" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_1" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
</ModelRoot>

View File

@ -1,13 +1,13 @@
<ModelRoot>
<ComputeNode hostname="hostname_0" uuid="Node_0" id="0" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_0" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_1" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_2" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_3" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_4" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_5" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_0" uuid="Node_0" id="0" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_0" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_1" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_2" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_3" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_4" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_5" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode hostname="hostname_1" uuid="Node_1" id="1" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64"/>
<ComputeNode hostname="hostname_2" uuid="Node_2" id="2" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64"/>
<ComputeNode hostname="hostname_3" uuid="Node_3" id="3" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64"/>
<ComputeNode hostname="hostname_1" uuid="Node_1" id="1" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64"/>
<ComputeNode hostname="hostname_2" uuid="Node_2" id="2" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64"/>
<ComputeNode hostname="hostname_3" uuid="Node_3" id="3" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64"/>
</ModelRoot>

View File

@ -1,8 +1,8 @@
<ModelRoot>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="73b09e16-35b7-4922-804e-e8f5d9b740fc" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="73b09e16-35b7-4922-804e-e8f5d9b740fc" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode human_id="" uuid="Node_1" status="enabled" state="up" id="1" hostname="hostname_1" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="a4cab39b-9828-413a-bf88-f76921bf1517" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_1" status="enabled" state="up" id="1" hostname="hostname_1" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="a4cab39b-9828-413a-bf88-f76921bf1517" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
</ModelRoot>

View File

@ -1,9 +1,9 @@
<ModelRoot>
<ComputeNode hostname="hostname_0" uuid="Node_0" id="0" state="up" human_id="" status="enabled" ResourceType.cpu_cores="10" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_6" ResourceType.cpu_cores="1" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_7" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_8" ResourceType.cpu_cores="4" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_9" ResourceType.cpu_cores="8" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_0" uuid="Node_0" id="0" state="up" human_id="" status="enabled" vcpus="10" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_6" vcpus="1" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_7" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_8" vcpus="4" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_9" vcpus="8" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode hostname="hostname_1" uuid="Node_1" id="1" state="up" human_id="" status="enabled" ResourceType.cpu_cores="10" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64"/>
<ComputeNode hostname="hostname_1" uuid="Node_1" id="1" state="up" human_id="" status="enabled" vcpus="10" disk="250" disk_capacity="250" memory="64"/>
</ModelRoot>

View File

@ -1,3 +1,3 @@
<ModelRoot>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" ResourceType.cpu_cores="1" ResourceType.disk="1" ResourceType.disk_capacity="1" ResourceType.memory="1"/>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" vcpus="1" disk="1" disk_capacity="1" memory="1"/>
</ModelRoot>

View File

@ -1,5 +1,5 @@
<ModelRoot>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" ResourceType.cpu_cores="4" ResourceType.disk="4" ResourceType.disk_capacity="4" ResourceType.memory="4">
<Instance state="active" human_id="" uuid="INSTANCE_0" hostname="" ResourceType.cpu_cores="4" ResourceType.disk="0" ResourceType.disk_capacity="0" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" vcpus="4" disk="4" disk_capacity="4" memory="4">
<Instance state="active" human_id="" uuid="INSTANCE_0" vcpus="4" disk="0" disk_capacity="0" memory="2"/>
</ComputeNode>
</ModelRoot>

View File

@ -1,10 +1,10 @@
<ModelRoot>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="73b09e16-35b7-4922-804e-e8f5d9b740fc" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_1" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="73b09e16-35b7-4922-804e-e8f5d9b740fc" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_1" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode human_id="" uuid="Node_1" status="enabled" state="up" id="1" hostname="hostname_1" ResourceType.cpu_cores="40" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_3" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_4" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_1" status="enabled" state="up" id="1" hostname="hostname_1" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_3" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_4" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
</ModelRoot>

View File

@ -1,10 +1,10 @@
<ModelRoot>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" ResourceType.cpu_cores="50" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="73b09e16-35b7-4922-804e-e8f5d9b740fc" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="cae81432-1631-4d4e-b29c-6f3acdcde906" hostname="" ResourceType.cpu_cores="15" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" vcpus="50" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="73b09e16-35b7-4922-804e-e8f5d9b740fc" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="cae81432-1631-4d4e-b29c-6f3acdcde906" vcpus="15" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode human_id="" uuid="Node_1" status="enabled" state="up" id="1" hostname="hostname_1" ResourceType.cpu_cores="50" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_3" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_4" hostname="" ResourceType.cpu_cores="10" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode human_id="" uuid="Node_1" status="enabled" state="up" id="1" hostname="hostname_1" vcpus="50" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_3" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
<Instance state="active" human_id="" uuid="INSTANCE_4" vcpus="10" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
</ModelRoot>

View File

@ -1,16 +1,16 @@
<ModelRoot>
<ComputeNode hostname="hostname_0" uuid="Node_0" id="0" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_0" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_1" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_2" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_0" uuid="Node_0" id="0" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_0" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_1" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_2" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode hostname="hostname_1" uuid="Node_1" id="1" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_3" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_1" uuid="Node_1" id="1" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_3" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode hostname="hostname_2" uuid="Node_2" id="2" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_4" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_2" uuid="Node_2" id="2" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_4" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode hostname="hostname_3" uuid="Node_3" id="3" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_5" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_3" uuid="Node_3" id="3" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_5" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
</ModelRoot>

View File

@ -1,16 +1,16 @@
<ModelRoot>
<ComputeNode hostname="hostname_0" uuid="Node_0" id="0" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_0" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_1" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_2" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_0" uuid="Node_0" id="0" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_0" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_1" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
<Instance human_id="" state="active" uuid="INSTANCE_2" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode hostname="hostname_1" uuid="Node_1" id="1" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_3" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_1" uuid="Node_1" id="1" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_3" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode hostname="hostname_2" uuid="Node_2" id="2" state="up" human_id="" status="enabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_4" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_2" uuid="Node_2" id="2" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_4" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
<ComputeNode hostname="hostname_3" uuid="Node_3" id="3" state="up" human_id="" status="disabled" ResourceType.cpu_cores="16" ResourceType.disk="250" ResourceType.disk_capacity="250" ResourceType.memory="64">
<Instance hostname="" human_id="" state="active" uuid="INSTANCE_5" ResourceType.cpu_cores="2" ResourceType.disk="20" ResourceType.disk_capacity="20" ResourceType.memory="2"/>
<ComputeNode hostname="hostname_3" uuid="Node_3" id="3" state="up" human_id="" status="disabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
<Instance human_id="" state="active" uuid="INSTANCE_5" vcpus="2" disk="20" disk_capacity="20" memory="2"/>
</ComputeNode>
</ModelRoot>

View File

@ -102,11 +102,12 @@ class FakeCeilometerMetrics(object):
Returns relative node CPU utilization <0, 100>.
:param r_id: resource id
"""
uuid = '%s_%s' % (r_id.split('_')[0], r_id.split('_')[1])
instances = self.model.get_mapping().get_node_instances_by_uuid(uuid)
node_uuid = '%s_%s' % (r_id.split('_')[0], r_id.split('_')[1])
node = self.model.get_node_by_uuid(node_uuid)
instances = self.model.get_node_instances(node)
util_sum = 0.0
node_cpu_cores = self.model.get_resource_by_uuid(
element.ResourceType.cpu_cores).get_capacity_by_uuid(uuid)
element.ResourceType.cpu_cores).get_capacity_by_uuid(node.uuid)
for instance_uuid in instances:
instance_cpu_cores = self.model.get_resource_by_uuid(
element.ResourceType.cpu_cores).\

View File

@ -54,7 +54,7 @@ class FakerModelCollector(base.BaseClusterDataModelCollector):
def build_scenario_1(self):
instances = []
current_state_cluster = modelroot.ModelRoot()
model = modelroot.ModelRoot()
# number of nodes
node_count = 5
# number max of instance per node
@ -62,74 +62,52 @@ class FakerModelCollector(base.BaseClusterDataModelCollector):
# total number of virtual machine
instance_count = (node_count * node_instance_count)
# define ressouce ( CPU, MEM disk, ... )
mem = element.Resource(element.ResourceType.memory)
# 2199.954 Mhz
num_cores = element.Resource(element.ResourceType.cpu_cores)
disk = element.Resource(element.ResourceType.disk)
disk_capacity = element.Resource(element.ResourceType.disk_capacity)
current_state_cluster.create_resource(mem)
current_state_cluster.create_resource(num_cores)
current_state_cluster.create_resource(disk)
current_state_cluster.create_resource(disk_capacity)
for id_ in range(0, node_count):
node_uuid = "Node_{0}".format(id_)
node = element.ComputeNode(id_)
node.uuid = node_uuid
node.hostname = "hostname_{0}".format(id_)
mem.set_capacity(node, 132)
disk.set_capacity(node, 250)
disk_capacity.set_capacity(node, 250)
num_cores.set_capacity(node, 40)
current_state_cluster.add_node(node)
hostname = "hostname_{0}".format(id_)
node_attributes = {
"id": id_,
"uuid": node_uuid,
"hostname": hostname,
"memory": 132,
"disk": 250,
"disk_capacity": 250,
"vcpus": 40,
}
node = element.ComputeNode(**node_attributes)
model.add_node(node)
for i in range(0, instance_count):
instance_uuid = "INSTANCE_{0}".format(i)
instance = element.Instance()
instance.uuid = instance_uuid
mem.set_capacity(instance, 2)
disk.set_capacity(instance, 20)
disk_capacity.set_capacity(instance, 20)
num_cores.set_capacity(instance, 10)
instance_attributes = {
"uuid": instance_uuid,
"memory": 2,
"disk": 20,
"disk_capacity": 20,
"vcpus": 10,
}
instance = element.Instance(**instance_attributes)
instances.append(instance)
current_state_cluster.add_instance(instance)
model.add_instance(instance)
current_state_cluster.mapping.map(
current_state_cluster.get_node_by_uuid("Node_0"),
current_state_cluster.get_instance_by_uuid("INSTANCE_0"))
mappings = [
("INSTANCE_0", "Node_0"),
("INSTANCE_1", "Node_0"),
("INSTANCE_2", "Node_1"),
("INSTANCE_3", "Node_2"),
("INSTANCE_4", "Node_2"),
("INSTANCE_5", "Node_2"),
("INSTANCE_6", "Node_3"),
("INSTANCE_7", "Node_4"),
]
for instance_uuid, node_uuid in mappings:
model.map_instance(
model.get_instance_by_uuid(instance_uuid),
model.get_node_by_uuid(node_uuid),
)
current_state_cluster.mapping.map(
current_state_cluster.get_node_by_uuid("Node_0"),
current_state_cluster.get_instance_by_uuid("INSTANCE_1"))
current_state_cluster.mapping.map(
current_state_cluster.get_node_by_uuid("Node_1"),
current_state_cluster.get_instance_by_uuid("INSTANCE_2"))
current_state_cluster.mapping.map(
current_state_cluster.get_node_by_uuid("Node_2"),
current_state_cluster.get_instance_by_uuid("INSTANCE_3"))
current_state_cluster.mapping.map(
current_state_cluster.get_node_by_uuid("Node_2"),
current_state_cluster.get_instance_by_uuid("INSTANCE_4"))
current_state_cluster.mapping.map(
current_state_cluster.get_node_by_uuid("Node_2"),
current_state_cluster.get_instance_by_uuid("INSTANCE_5"))
current_state_cluster.mapping.map(
current_state_cluster.get_node_by_uuid("Node_3"),
current_state_cluster.get_instance_by_uuid("INSTANCE_6"))
current_state_cluster.mapping.map(
current_state_cluster.get_node_by_uuid("Node_4"),
current_state_cluster.get_instance_by_uuid("INSTANCE_7"))
return current_state_cluster
return model
def generate_scenario_1(self):
return self.load_model('scenario_1.xml')

View File

@ -26,7 +26,6 @@ from watcher.common import exception
from watcher.common import nova_helper
from watcher.common import service as watcher_service
from watcher.decision_engine.model import element
from watcher.decision_engine.model import model_root
from watcher.decision_engine.model.notification import nova as novanotification
from watcher.tests import base as base_test
from watcher.tests.decision_engine.model import faker_cluster_state
@ -35,7 +34,8 @@ from watcher.tests.decision_engine.model.notification import fake_managers
class NotificationTestCase(base_test.TestCase):
def load_message(self, filename):
@staticmethod
def load_message(filename):
cwd = os.path.abspath(os.path.dirname(__file__))
data_folder = os.path.join(cwd, "data")
@ -188,6 +188,9 @@ class TestNovaNotifications(NotificationTestCase):
side_effect=lambda uuid: mock.Mock(
name='m_get_compute_node_by_hostname',
id=3,
hypervisor_hostname="Node_2",
state='up',
status='enabled',
uuid=uuid,
memory_mb=7777,
vcpus=42,
@ -215,7 +218,7 @@ class TestNovaNotifications(NotificationTestCase):
instance0 = compute_model.get_instance_by_uuid(instance0_uuid)
cpu_capacity = compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores)
element.ResourceType.vcpus)
disk = compute_model.get_resource_by_uuid(
element.ResourceType.disk)
disk_capacity = compute_model.get_resource_by_uuid(
@ -239,7 +242,7 @@ class TestNovaNotifications(NotificationTestCase):
def test_instance_update_node_notfound_set_unmapped(
self, m_nova_helper_cls):
m_get_compute_node_by_hostname = mock.Mock(
side_effect=exception.ComputeNodeNotFound)
side_effect=exception.ComputeNodeNotFound(name="TEST"))
m_nova_helper_cls.return_value = mock.Mock(
get_compute_node_by_hostname=m_get_compute_node_by_hostname,
name='m_nova_helper')
@ -263,7 +266,7 @@ class TestNovaNotifications(NotificationTestCase):
instance0 = compute_model.get_instance_by_uuid(instance0_uuid)
cpu_capacity = compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores)
element.ResourceType.vcpus)
disk = compute_model.get_resource_by_uuid(
element.ResourceType.disk)
disk_capacity = compute_model.get_resource_by_uuid(
@ -304,7 +307,7 @@ class TestNovaNotifications(NotificationTestCase):
instance0 = compute_model.get_instance_by_uuid(instance0_uuid)
cpu_capacity = compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores)
element.ResourceType.vcpus)
disk_capacity = compute_model.get_resource_by_uuid(
element.ResourceType.disk)
memory_capacity = compute_model.get_resource_by_uuid(
@ -324,8 +327,6 @@ class TestNovaNotifications(NotificationTestCase):
# Before
self.assertTrue(compute_model.get_instance_by_uuid(instance0_uuid))
for resource in compute_model.resource.values():
self.assertIn(instance0_uuid, resource.mapping)
message = self.load_message('scenario3_instance-delete-end.json')
handler.info(
@ -341,9 +342,6 @@ class TestNovaNotifications(NotificationTestCase):
exception.InstanceNotFound,
compute_model.get_instance_by_uuid, instance0_uuid)
for resource in compute_model.resource.values():
self.assertNotIn(instance0_uuid, resource.mapping)
class TestLegacyNovaNotifications(NotificationTestCase):
@ -377,7 +375,7 @@ class TestLegacyNovaNotifications(NotificationTestCase):
instance0 = compute_model.get_instance_by_uuid(instance0_uuid)
cpu_capacity = compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores)
element.ResourceType.vcpus)
disk_capacity = compute_model.get_resource_by_uuid(
element.ResourceType.disk)
memory_capacity = compute_model.get_resource_by_uuid(
@ -410,31 +408,6 @@ class TestLegacyNovaNotifications(NotificationTestCase):
self.assertEqual(element.InstanceState.PAUSED.value, instance0.state)
def test_legacy_instance_update_instance_notfound_creates(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.LegacyInstanceUpdated(self.fake_cdmc)
instance0_uuid = '73b09e16-35b7-4922-804e-e8f5d9b740fc'
message = self.load_message('scenario3_legacy_instance-update.json')
with mock.patch.object(
model_root.ModelRoot, 'get_instance_by_uuid'
) as m_get_instance_by_uuid:
m_get_instance_by_uuid.side_effect = exception.InstanceNotFound(
name='TEST')
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
instance0 = compute_model.get_instance_by_uuid(instance0_uuid)
self.assertEqual(element.InstanceState.PAUSED.value, instance0.state)
@mock.patch.object(nova_helper, "NovaHelper")
def test_legacy_instance_update_node_notfound_still_creates(
self, m_nova_helper_cls):
@ -443,6 +416,9 @@ class TestLegacyNovaNotifications(NotificationTestCase):
name='m_get_compute_node_by_hostname',
id=3,
uuid=uuid,
hypervisor_hostname="Node_2",
state='up',
status='enabled',
memory_mb=7777,
vcpus=42,
free_disk_gb=974,
@ -470,7 +446,7 @@ class TestLegacyNovaNotifications(NotificationTestCase):
instance0 = compute_model.get_instance_by_uuid(instance0_uuid)
cpu_capacity = compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores)
element.ResourceType.vcpus)
disk = compute_model.get_resource_by_uuid(
element.ResourceType.disk)
disk_capacity = compute_model.get_resource_by_uuid(
@ -519,7 +495,7 @@ class TestLegacyNovaNotifications(NotificationTestCase):
instance0 = compute_model.get_instance_by_uuid(instance0_uuid)
cpu_capacity = compute_model.get_resource_by_uuid(
element.ResourceType.cpu_cores)
element.ResourceType.vcpus)
disk = compute_model.get_resource_by_uuid(
element.ResourceType.disk)
disk_capacity = compute_model.get_resource_by_uuid(
@ -571,8 +547,6 @@ class TestLegacyNovaNotifications(NotificationTestCase):
# Before
self.assertTrue(compute_model.get_instance_by_uuid(instance0_uuid))
for resource in compute_model.resource.values():
self.assertIn(instance0_uuid, resource.mapping)
message = self.load_message(
'scenario3_legacy_instance-delete-end.json')
@ -588,6 +562,3 @@ class TestLegacyNovaNotifications(NotificationTestCase):
self.assertRaises(
exception.InstanceNotFound,
compute_model.get_instance_by_uuid, instance0_uuid)
for resource in compute_model.resource.values():
self.assertNotIn(instance0_uuid, resource.mapping)

View File

@ -1,33 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
from watcher.decision_engine.model import element
from watcher.tests import base
class TestDiskInfo(base.TestCase):
def test_all(self):
disk_information = element.DiskInfo()
disk_information.set_size(1024)
self.assertEqual(1024, disk_information.get_size())
disk_information.set_scheduler = "scheduler_qcq"
disk_information.set_device_name("nom_qcq")
self.assertEqual("nom_qcq", disk_information.get_device_name())

View File

@ -0,0 +1,72 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2017 b<>com
#
# Authors: Vincent FRANCOISE <Vincent.FRANCOISE@b-com.com>
#
# 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.
from watcher.decision_engine.model import element
from watcher.tests import base
class TestElement(base.TestCase):
scenarios = [
("ComputeNode_with_all_fields", dict(
cls=element.Instance,
data={
'uuid': 'FAKE_UUID',
'state': 'state',
'hostname': 'hostname',
'human_id': 'human_id',
'memory': 111,
'vcpus': 222,
'disk': 333,
'disk_capacity': 444,
})),
("ComputeNode_with_some_fields", dict(
cls=element.Instance,
data={
'uuid': 'FAKE_UUID',
'state': 'state',
'vcpus': 222,
'disk': 333,
'disk_capacity': 444,
})),
("Instance_with_all_fields", dict(
cls=element.Instance,
data={
'uuid': 'FAKE_UUID',
'state': 'state',
'hostname': 'hostname',
'human_id': 'human_id',
'memory': 111,
'vcpus': 222,
'disk': 333,
'disk_capacity': 444,
})),
("Instance_with_some_fields", dict(
cls=element.Instance,
data={
'uuid': 'FAKE_UUID',
'state': 'state',
'vcpus': 222,
'disk': 333,
'disk_capacity': 444,
})),
]
def test_as_xml_element(self):
el = self.cls(**self.data)
el.as_xml_element()

View File

@ -1,30 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
from watcher.decision_engine.model import element
from watcher.tests import base
class TestInstance(base.TestCase):
def test_namedelement(self):
instance = element.Instance()
instance.state = element.InstanceState.ACTIVE
self.assertEqual(element.InstanceState.ACTIVE, instance.state)
instance.human_id = "human_05"
self.assertEqual("human_05", instance.human_id)

View File

@ -1,113 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
from oslo_utils import uuidutils
from watcher.decision_engine.model import element
from watcher.tests import base
from watcher.tests.decision_engine.model import faker_cluster_state
class TestMapping(base.TestCase):
INST1_UUID = "73b09e16-35b7-4922-804e-e8f5d9b740fc"
INST2_UUID = "a4cab39b-9828-413a-bf88-f76921bf1517"
def setUp(self):
super(TestMapping, self).setUp()
self.fake_cluster = faker_cluster_state.FakerModelCollector()
def test_get_node_from_instance(self):
model = self.fake_cluster.generate_scenario_3_with_2_nodes()
instances = model.get_all_instances()
keys = list(instances.keys())
instance = instances[keys[0]]
if instance.uuid != self.INST1_UUID:
instance = instances[keys[1]]
node = model.mapping.get_node_from_instance(instance)
self.assertEqual('Node_0', node.uuid)
def test_get_node_by_instance_uuid(self):
model = self.fake_cluster.generate_scenario_3_with_2_nodes()
nodes = model.mapping.get_node_instances_by_uuid("BLABLABLA")
self.assertEqual(0, len(nodes))
def test_get_all_instances(self):
model = self.fake_cluster.generate_scenario_3_with_2_nodes()
instances = model.get_all_instances()
self.assertEqual(2, len(instances))
self.assertEqual(element.InstanceState.ACTIVE.value,
instances[self.INST1_UUID].state)
self.assertEqual(self.INST1_UUID, instances[self.INST1_UUID].uuid)
self.assertEqual(element.InstanceState.ACTIVE.value,
instances[self.INST2_UUID].state)
self.assertEqual(self.INST2_UUID, instances[self.INST2_UUID].uuid)
def test_get_mapping(self):
model = self.fake_cluster.generate_scenario_3_with_2_nodes()
instance_mapping = model.mapping.instance_mapping
self.assertEqual(2, len(instance_mapping))
self.assertEqual('Node_0', instance_mapping[self.INST1_UUID])
self.assertEqual('Node_1', instance_mapping[self.INST2_UUID])
def test_migrate_instance(self):
model = self.fake_cluster.generate_scenario_3_with_2_nodes()
instances = model.get_all_instances()
keys = list(instances.keys())
instance0 = instances[keys[0]]
node0 = model.mapping.get_node_by_instance_uuid(instance0.uuid)
instance1 = instances[keys[1]]
node1 = model.mapping.get_node_by_instance_uuid(instance1.uuid)
self.assertEqual(
False,
model.migrate_instance(instance1, node1, node1))
self.assertEqual(
False,
model.migrate_instance(instance1, node0, node0))
self.assertEqual(
True,
model.migrate_instance(instance1, node1, node0))
self.assertEqual(
True,
model.migrate_instance(instance1, node0, node1))
def test_unmap_by_uuid_log_warning(self):
model = self.fake_cluster.generate_scenario_3_with_2_nodes()
instances = model.get_all_instances()
keys = list(instances.keys())
instance0 = instances[keys[0]]
uuid_ = uuidutils.generate_uuid()
node = element.ComputeNode(id=1)
node.uuid = uuid_
model.mapping.unmap_by_uuid(node.uuid, instance0.uuid)
def test_unmap_by_uuid(self):
model = self.fake_cluster.generate_scenario_3_with_2_nodes()
instances = model.get_all_instances()
keys = list(instances.keys())
instance0 = instances[keys[0]]
node0 = model.mapping.get_node_by_instance_uuid(instance0.uuid)
model.mapping.unmap_by_uuid(node0.uuid, instance0.uuid)
self.assertEqual(0, len(model.mapping.get_node_instances_by_uuid(
node0.uuid)))

View File

@ -47,9 +47,9 @@ class TestModel(base.TestCase):
fake_cluster = faker_cluster_state.FakerModelCollector()
model = fake_cluster.build_scenario_1()
self.assertEqual(5, len(model._nodes))
self.assertEqual(35, len(model._instances))
self.assertEqual(5, len(model.mapping.get_mapping()))
self.assertEqual(5, len(model.get_all_compute_nodes()))
self.assertEqual(35, len(model.get_all_instances()))
self.assertEqual(8, len(model.edges()))
expected_struct_str = self.load_data('scenario_1.xml')
parser = etree.XMLParser(remove_blank_text=True)

View File

@ -42,34 +42,37 @@ class TestDefaultScope(base.TestCase):
for i in range(2)]
model = default.DefaultScope(audit_scope,
osc=mock.Mock()).get_scoped_model(cluster)
nodes = {'Node_4': set([]), 'Node_0': set([]), 'Node_3': set([]),
'Node_1': set(['INSTANCE_2']), 'Node_2': set([])}
self.assertEqual(nodes, model.get_mapping().get_mapping())
expected_edges = [('INSTANCE_2', 'Node_1')]
edges = [(src.uuid, dst.uuid) for src, dst in model.edges()]
self.assertEqual(sorted(expected_edges), sorted(edges))
@mock.patch.object(nova_helper.NovaHelper, 'get_availability_zone_list')
def test_get_scoped_model_without_scope(self, mock_zone_list):
cluster = self.fake_cluster.generate_scenario_1()
model = self.fake_cluster.generate_scenario_1()
default.DefaultScope([],
osc=mock.Mock()).get_scoped_model(cluster)
osc=mock.Mock()).get_scoped_model(model)
assert not mock_zone_list.called
def test__remove_instance(self):
cluster = self.fake_cluster.generate_scenario_1()
default.DefaultScope([],
osc=mock.Mock())._remove_instance(cluster,
'INSTANCE_2',
'Node_1')
expected_map = {'Node_4': set(['INSTANCE_7']), 'Node_1': set([]),
'Node_0': set(['INSTANCE_0', 'INSTANCE_1']),
'Node_3': set(['INSTANCE_6']),
'Node_2': set(['INSTANCE_4', 'INSTANCE_5',
'INSTANCE_3'])}
self.assertEqual(expected_map, cluster.get_mapping().get_mapping())
def test_remove_instance(self):
model = self.fake_cluster.generate_scenario_1()
default.DefaultScope([], osc=mock.Mock()).remove_instance(
model, model.get_instance_by_uuid('INSTANCE_2'), 'Node_1')
expected_edges = [
('INSTANCE_0', 'Node_0'),
('INSTANCE_1', 'Node_0'),
('INSTANCE_3', 'Node_2'),
('INSTANCE_4', 'Node_2'),
('INSTANCE_5', 'Node_2'),
('INSTANCE_6', 'Node_3'),
('INSTANCE_7', 'Node_4'),
]
edges = [(src.uuid, dst.uuid) for src, dst in model.edges()]
self.assertEqual(sorted(expected_edges), sorted(edges))
@mock.patch.object(nova_helper.NovaHelper, 'get_aggregate_detail')
@mock.patch.object(nova_helper.NovaHelper, 'get_aggregate_list')
def test__collect_aggregates(self, mock_aggregate,
mock_detailed_aggregate):
def test_collect_aggregates(self, mock_aggregate, mock_detailed_aggregate):
allowed_nodes = []
mock_aggregate.return_value = [mock.Mock(id=i) for i in range(2)]
mock_detailed_aggregate.side_effect = [
@ -126,12 +129,12 @@ class TestDefaultScope(base.TestCase):
self.assertEqual(['Node_0', 'Node_1'], allowed_nodes)
@mock.patch.object(nova_helper.NovaHelper, 'get_availability_zone_list')
def test__collect_zones(self, mock_zone_list):
def test_collect_zones(self, mock_zone_list):
allowed_nodes = []
mock_zone_list.return_value = [
mock.Mock(zoneName="AZ{0}".format(i+1),
hosts={'Node_{0}'.format(2*i): 1,
'Node_{0}'.format(2*i+1): 2})
mock.Mock(zoneName="AZ{0}".format(i + 1),
hosts={'Node_{0}'.format(2 * i): 1,
'Node_{0}'.format(2 * i + 1): 2})
for i in range(2)]
default.DefaultScope([{'availability_zones': [{'name': "AZ1"}]}],
osc=mock.Mock())._collect_zones(
@ -142,9 +145,9 @@ class TestDefaultScope(base.TestCase):
def test_zones_wildcard_is_used(self, mock_zone_list):
allowed_nodes = []
mock_zone_list.return_value = [
mock.Mock(zoneName="AZ{0}".format(i+1),
hosts={'Node_{0}'.format(2*i): 1,
'Node_{0}'.format(2*i+1): 2})
mock.Mock(zoneName="AZ{0}".format(i + 1),
hosts={'Node_{0}'.format(2 * i): 1,
'Node_{0}'.format(2 * i + 1): 2})
for i in range(2)]
default.DefaultScope([{'availability_zones': [{'name': "*"}]}],
osc=mock.Mock())._collect_zones(
@ -156,9 +159,9 @@ class TestDefaultScope(base.TestCase):
def test_zones_wildcard_with_other_ids(self, mock_zone_list):
allowed_nodes = []
mock_zone_list.return_value = [
mock.Mock(zoneName="AZ{0}".format(i+1),
hosts={'Node_{0}'.format(2*i): 1,
'Node_{0}'.format(2*i+1): 2})
mock.Mock(zoneName="AZ{0}".format(i + 1),
hosts={'Node_{0}'.format(2 * i): 1,
'Node_{0}'.format(2 * i + 1): 2})
for i in range(2)]
scope_handler = default.DefaultScope(
[{'availability_zones': [{'name': "*"}, {'name': 'AZ1'}]}],
@ -173,38 +176,42 @@ class TestDefaultScope(base.TestCase):
validators.Draft4Validator(
default.DefaultScope.DEFAULT_SCHEMA).validate(test_scope)
def test__exclude_resources(self):
def test_exclude_resources(self):
resources_to_exclude = [{'instances': [{'uuid': 'INSTANCE_1'},
{'uuid': 'INSTANCE_2'}]},
{'compute_nodes': [{'name': 'Node_1'},
{'name': 'Node_2'}]}
]
{'name': 'Node_2'}]}]
instances_to_exclude = []
nodes_to_exclude = []
default.DefaultScope([], osc=mock.Mock())._exclude_resources(
default.DefaultScope([], osc=mock.Mock()).exclude_resources(
resources_to_exclude, instances=instances_to_exclude,
nodes=nodes_to_exclude)
self.assertEqual(['Node_1', 'Node_2'], sorted(nodes_to_exclude))
self.assertEqual(['INSTANCE_1', 'INSTANCE_2'],
sorted(instances_to_exclude))
def test__remove_node_from_model(self):
cluster = self.fake_cluster.generate_scenario_1()
default.DefaultScope([], osc=mock.Mock())._remove_node_from_model(
['Node_1', 'Node_2'], cluster)
expected_cluster = {'Node_0': set(['INSTANCE_0', 'INSTANCE_1']),
'Node_1': set([]), 'Node_2': set([]),
'Node_3': set(['INSTANCE_6']),
'Node_4': set(['INSTANCE_7'])}
self.assertEqual(expected_cluster, cluster.get_mapping().get_mapping())
def test_remove_nodes_from_model(self):
model = self.fake_cluster.generate_scenario_1()
default.DefaultScope([], osc=mock.Mock()).remove_nodes_from_model(
['Node_1', 'Node_2'], model)
expected_edges = [
('INSTANCE_0', 'Node_0'),
('INSTANCE_1', 'Node_0'),
('INSTANCE_6', 'Node_3'),
('INSTANCE_7', 'Node_4')]
edges = [(src.uuid, dst.uuid) for src, dst in model.edges()]
self.assertEqual(sorted(expected_edges), sorted(edges))
def test__remove_instances_from_model(self):
cluster = self.fake_cluster.generate_scenario_1()
default.DefaultScope([], osc=mock.Mock())._remove_instances_from_model(
['INSTANCE_1', 'INSTANCE_2'], cluster)
expected_cluster = {'Node_0': set(['INSTANCE_0']), 'Node_1': set([]),
'Node_2': set(['INSTANCE_3', 'INSTANCE_4',
'INSTANCE_5']),
'Node_3': set(['INSTANCE_6']),
'Node_4': set(['INSTANCE_7'])}
self.assertEqual(expected_cluster, cluster.get_mapping().get_mapping())
def test_remove_instances_from_model(self):
model = self.fake_cluster.generate_scenario_1()
default.DefaultScope([], osc=mock.Mock()).remove_instances_from_model(
['INSTANCE_1', 'INSTANCE_2'], model)
expected_edges = [
('INSTANCE_0', 'Node_0'),
('INSTANCE_3', 'Node_2'),
('INSTANCE_4', 'Node_2'),
('INSTANCE_5', 'Node_2'),
('INSTANCE_6', 'Node_3'),
('INSTANCE_7', 'Node_4')]
edges = [(src.uuid, dst.uuid) for src, dst in model.edges()]
self.assertEqual(sorted(expected_edges), sorted(edges))

View File

@ -130,7 +130,7 @@ class TestUniformAirflow(base.TestCase):
self.strategy.threshold_inlet_t = 22
n1, n2 = self.strategy.group_hosts_by_airflow()
instances = model.get_all_instances()
instances.clear()
[model.remove_instance(inst) for inst in instances.values()]
instance_to_mig = self.strategy.choose_instance_to_migrate(n1)
self.assertIsNone(instance_to_mig)

View File

@ -127,7 +127,8 @@ class TestVMWorkloadConsolidation(base.TestCase):
n1 = model.get_node_by_uuid('Node_0')
n2 = model.get_node_by_uuid('Node_1')
instance_uuid = 'INSTANCE_0'
self.strategy.add_migration(instance_uuid, n1, n2, model)
instance = model.get_instance_by_uuid(instance_uuid)
self.strategy.add_migration(instance, n1, n2, model)
self.assertEqual(1, len(self.strategy.solution.actions))
expected = {'action_type': 'migrate',
'input_parameters': {'destination_node': n2.uuid,
@ -196,11 +197,12 @@ class TestVMWorkloadConsolidation(base.TestCase):
n1 = model.get_node_by_uuid('Node_0')
n2 = model.get_node_by_uuid('Node_1')
instance_uuid = 'INSTANCE_0'
instance = model.get_instance_by_uuid(instance_uuid)
self.strategy.disable_unused_nodes(model)
self.assertEqual(0, len(self.strategy.solution.actions))
# Migrate VM to free the node
self.strategy.add_migration(instance_uuid, n1, n2, model)
self.strategy.add_migration(instance, n1, n2, model)
self.strategy.disable_unused_nodes(model)
expected = {'action_type': 'change_nova_service_state',

View File

@ -74,7 +74,7 @@ class TestWorkloadBalance(base.TestCase):
model = self.fake_cluster.generate_scenario_6_with_2_nodes()
self.m_model.return_value = model
node = model.get_node_by_uuid('Node_0')
cap_cores = model.get_resource_by_uuid(element.ResourceType.cpu_cores)
cap_cores = model.get_resource_by_uuid(element.ResourceType.vcpus)
cap_mem = model.get_resource_by_uuid(element.ResourceType.memory)
cap_disk = model.get_resource_by_uuid(element.ResourceType.disk)
cores_used, mem_used, disk_used = (
@ -107,7 +107,7 @@ class TestWorkloadBalance(base.TestCase):
self.m_model.return_value = model
n1, n2, avg, w_map = self.strategy.group_hosts_by_cpu_util()
instances = model.get_all_instances()
instances.clear()
[model.remove_instance(inst) for inst in instances.values()]
instance_to_mig = self.strategy.choose_instance_to_migrate(
n1, avg, w_map)
self.assertIsNone(instance_to_mig)