diff --git a/requirements.txt b/requirements.txt index d3cc808d0..8aabaeb04 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,4 +20,5 @@ pytz>=2010h # for SECURE_KEY generation lockfile>=0.8 +python-ironicclient>=0.1.0 -e git://github.com/openstack/python-tuskarclient.git#egg=python-tuskarclient diff --git a/tuskar_ui/api.py b/tuskar_ui/api.py index ca283e653..7f0f71ea5 100644 --- a/tuskar_ui/api.py +++ b/tuskar_ui/api.py @@ -12,27 +12,25 @@ # License for the specific language governing permissions and limitations # under the License. -import collections -import copy -import datetime import logging -import random import django.conf -import django.db.models -from django.utils.translation import ugettext_lazy as _ # noqa -from novaclient.v1_1.contrib import baremetal + from openstack_dashboard.api import base -from openstack_dashboard.api import nova -import requests -from tuskarclient.v1 import client as tuskar_client - +from openstack_dashboard.test.test_data import utils from tuskar_ui.cached_property import cached_property # noqa - +from tuskar_ui.test.test_data import tuskar_data +from tuskarclient.v1 import client as tuskar_client LOG = logging.getLogger(__name__) TUSKAR_ENDPOINT_URL = getattr(django.conf.settings, 'TUSKAR_ENDPOINT_URL') -OVERCLOUD_CREDS = getattr(django.conf.settings, 'OVERCLOUD_CREDS', False) + + +# TODO(Tzu-Mainn Chen): remove test data when possible +def test_data(): + test_data = utils.TestDataContainer() + tuskar_data.data(test_data) + return test_data # FIXME: request isn't used right in the tuskar client right now, but looking @@ -42,756 +40,273 @@ def tuskarclient(request): return c -def baremetalclient(request): - insecure = getattr(django.conf.settings, 'OPENSTACK_SSL_NO_VERIFY', False) - nc = nova.nova_client.Client( - request.user.username, - request.user.token.id, - project_id=request.user.tenant_id, - auth_url=base.url_for(request, 'compute'), - insecure=insecure, - http_log_debug=django.conf.settings.DEBUG) - nc.client.auth_token = request.user.token.id - nc.client.management_url = base.url_for(request, 'compute') +class Stack(base.APIResourceWrapper): + _attrs = ('id', 'stack_name', 'stack_status') - LOG.debug('nova baremetal client connection created using token "%s" ' - 'and url "%s"' % - (request.user.token.id, base.url_for(request, 'compute'))) + @classmethod + def create(cls, request, stack_sizing): + # Assumptions: + # * hard-coded stack name ('overcloud') + # * there is a Tuskar API/library that puts + # together the Heat template + # Questions: + # * is the assumption correct, or does the UI have to + # do more heavy lifting? + # Required: + # * Stack sizing information + # Side Effects: + # * call out to Tuskar API/library, which deploys + # an 'overcloud' Stack using the sizing information + # Return: + # * the created Stack object - return baremetal.BareMetalNodeManager(nc) + # TODO(Tzu-Mainn Chen): remove test data when possible + # stack = tuskarclient(request).stacks.create( + # 'overcloud', + # stack_sizing) + stack = test_data().heatclient_stacks.first + + return cls(stack) + + @classmethod + def get(cls, request): + # Assumptions: + # * hard-coded stack name ('overcloud') + # Return: + # * the 'overcloud' Stack object + + # TODO(Tzu-Mainn Chen): remove test data when possible + # stack = heatclient(request).stacks.get('overcloud') + stack = test_data().heatclient_stacks.first + + return cls(stack) + + @cached_property + def resources(self): + # Assumptions: + # * hard-coded stack name ('overcloud') + # Return: + # * a list of Resources associated with the Stack + + # TODO(Tzu-Mainn Chen): remove test data when possible + # resources = heatclient(request).resources.list(self.id) + resources = test_data().heatclient_resources.list + + return [Resource(r) for r in resources] + + @cached_property + def nodes(self): + # Assumptions: + # * hard-coded stack name ('overcloud') + # Return: + # * a list of Nodes indirectly associated with the Stack + + return [resource.node for resource in self.resources] -def overcloudclient(request): - c = nova.nova_client.Client(OVERCLOUD_CREDS['user'], - OVERCLOUD_CREDS['password'], - OVERCLOUD_CREDS['tenant'], - auth_url=OVERCLOUD_CREDS['auth_url']) - return c +class Node(base.APIResourceWrapper): + _attrs = ('uuid', 'instance_uuid', 'driver', 'driver_info', + 'properties', 'power_state') + @classmethod + def create(cls, request, ipmi_address, cpu, ram, local_disk, + mac_addresses, ipmi_username=None, ipm_password=None): + # Questions: + # * what parameters can we pass in? + # Required: + # * ipmi_address, cpu, ram (GB), local_disk (TB), mac_address + # Optional: + # * ipmi_username, ipmi_password + # Side Effects: + # * call out to Ironic to registers a Node with the given + # parameters. Use a default chassis and create ports + # as needed + # Return: + # * the registered Node -class StringIdAPIResourceWrapper(base.APIResourceWrapper): - # horizon DataTable class expects ids to be string, - # if it's not string, then comparison in - # horizon/tables/base.py:get_object_by_id fails. - # Because of this, ids returned from dummy api are converted to string - # (luckily django autoconverts strings to integers when passing string to - # django model id) + # TODO(Tzu-Mainn Chen): remove test data when possible + # TODO(Tzu-Mainn Chen): transactionality? + # chassis = Node.default_chassis + # node = ironicclient(request).node.create( + # chassis_uuid=chassis.uuid, + # driver='pxe_ipmitool', + # driver_info={'ipmi_address': ipmi_address, + # 'ipmi_username': ipmi_username, + # 'password': ipmi_password}, + # properties={'cpu': cpu, + # 'ram': ram, + # 'local_disk': local_disk}) + # for mac_address in mac_addresses: + # ironicclient(request).port.create( + # node_uuid=node.uuid, + # address=mac_address + # ) + node = test_data().ironicclient_nodes.first - def __init__(self, apiresource, request=None): - self.request = request - self._apiresource = apiresource + return cls(node) - # FIXME - # this is redefined from base.APIResourceWrapper, - # remove this when tuskarclient returns object instead of dict - def __getattr__(self, attr): - if attr in self._attrs: - if issubclass(self._apiresource.__class__, dict): - return self._apiresource.get(attr) + @classmethod + def get(cls, request, uuid): + # Required: + # * uuid + # Return: + # * the Node associated with the uuid + + # TODO(Tzu-Mainn Chen): remove test data when possible + # node = ironicclient(request).nodes.get(uuid) + node = test_data().ironicclient_nodes.first + + return cls(node) + + @classmethod + def list(cls, request, associated=None): + # Optional: + # * free + # Return: + # * a list of Nodes registered in Ironic. + + # TODO(Tzu-Mainn Chen): remove test data when possible + # nodes = ironicclient(request).nodes.list( + # associated=associated) + + nodes = test_data().ironicclient_nodes.list() + if associated is not None: + if associated: + nodes = [node for node in nodes + if node.instance_uuid is not None] else: - try: - return self._apiresource.__getattribute__(attr) - except AttributeError: - return None - else: - raise AttributeError(attr) + nodes = [node for node in nodes + if node.instance_uuid is None] - @property - def id(self): - return str(self._apiresource.id) - - # FIXME: self.request is required when calling some instance - # methods (e.g. list_flavors), once we really start using this request - # param (if ever), a proper request value should be set - @property - def request(self): - return getattr(self, '_request', None) - - @request.setter - def request(self, value): - setattr(self, '_request', value) - - -class Alert(StringIdAPIResourceWrapper): - """Wrapper for the Alert object returned by the - dummy model. - """ - _attrs = ['message', 'time'] - - -class Capacity(StringIdAPIResourceWrapper): - """Wrapper for the Capacity object returned by the - dummy model. - """ - _attrs = ['name', 'value', 'unit'] - - # defines a random usage of capacity - API should probably be able to - # determine usage of capacity based on capacity value and obejct_id - @cached_property - def usage(self): - return random.randint(0, int(self.value)) - - # defines a random average of capacity - API should probably be able to - # determine average of capacity based on capacity value and obejct_id - @cached_property - def average(self): - return random.randint(0, int(self.value)) - - -class BaremetalNode(StringIdAPIResourceWrapper): - _attrs = ['id', 'pm_address', 'cpus', 'memory_mb', 'service_host', - 'local_gb', 'pm_user', 'instance_uuid'] + return [cls(node) for node in nodes] @classmethod - def create(cls, request, **kwargs): - # The pm_address, pm_user and terminal_port need to be None when - # empty for the baremetal vm to work. - # terminal_port needs separate handling because 0 is a valid value. - terminal_port = kwargs['terminal_port'] - if terminal_port == '': - terminal_port = None - baremetal_node = baremetalclient(request).create( - kwargs['service_host'], - kwargs['cpus'], - kwargs['memory_mb'], - kwargs['local_gb'], - kwargs['prov_mac_address'], - kwargs['pm_address'] or None, - kwargs['pm_user'] or None, - kwargs['pm_password'], - terminal_port) - return cls(baremetal_node) + def delete(cls, request, uuid): + # Required: + # * uuid + # Side Effects: + # * remove the Node with the associated uuid from + # Ironic + + # TODO(Tzu-Mainn Chen): uncomment when possible + # ironicclient(request).nodes.delete(uuid) + return @classmethod - def get(cls, request, baremetal_node_id): - baremetal_node = cls(baremetalclient(request).get(baremetal_node_id)) - baremetal_node.request = request + def default_chassis(cls, request): + # Return: + # * the default chassis uses for all nodes in Tuskar + # Side Effects: + # * if a chassis doesn't exist, creates it in Ironic + # first - try: - # Nova instance info will be added to baremetal node attributes - nova_instance = nova.server_get(request, - baremetal_node.instance_uuid) - except Exception: - nova_instance = None - LOG.debug("Couldn't obtain nova.server_get instance for " - "baremetal node %s" % baremetal_node_id) - if nova_instance: - # If baremetal is provisioned, there is a nova instance. - addresses = nova_instance._apiresource.addresses.get('ctlplane') - if addresses: - baremetal_node.ip_address_other = ", ".join( - [addr['addr'] for addr in addresses]) - baremetal_node.status = nova_instance._apiresource._info[ - 'OS-EXT-STS:vm_state'] - baremetal_node.power_management = "" - if baremetal_node.pm_user: - baremetal_node.power_management = ( - baremetal_node.pm_user + "/********") - else: - # If baremetal is unprovisioned, there is no nova instance. - baremetal_node.status = 'unprovisioned' + # TODO(Tzu-Mainn Chen): uncomment when possible + # TODO(Tzu-Mainn Chen): possible race condition + #chassis_list = ironicclient(request).chassis.list() + #if not chassis_list: + # chassis = ironicclient(request).chassis.create( + # description='Default Chassis') + #else: + # chassis = chassis_list[0] + chassis = test_data().ironicclient_chassis.list()[0] - # Returning baremetal node containing nova instance info - return baremetal_node - - @classmethod - def list(cls, request): - return [cls(n, request) for n in - baremetalclient(request).list()] - - @classmethod - def list_unracked(cls, request): - try: - racked_baremetal_node_ids = [ - tuskar_node.nova_baremetal_node_id - for tuskar_node in TuskarNode.list(request)] - return [baremetal_node - for baremetal_node in BaremetalNode.list(request) - if baremetal_node.id not in racked_baremetal_node_ids] - except requests.ConnectionError: - return [] + return chassis @cached_property - def tuskar_node(self): - node = next((tuskar_node - for tuskar_node in TuskarNode.list(self.request) - if tuskar_node.nova_baremetal_node_id == self.id), + def resource(self, stack): + # Questions: + # * can we assume one resource per Node? + # Required: + # * stack + # Return: + # * return the node's associated Resource within the passed-in + # stack, if any + + return next((r for r in stack.resources + if r.physical_resource_id == self.instance_uuid), None) - return node - - @property - def mac_address(self): - try: - return self._apiresource.interfaces[0]['address'] - except Exception: - return None - - @property - # FIXME: just mock implementation, add proper one - def running_instances(self): - return 4 - - @property - # FIXME: just mock implementation, add proper one - def remaining_capacity(self): - return 100 - self.running_instances @cached_property - def running_virtual_machines(self): - if OVERCLOUD_CREDS: - search_opts = {} - search_opts['all_tenants'] = True - return [ - s for s in overcloudclient( - self.request).servers.list(True, search_opts) - if s.hostId == self.id] - else: - LOG.debug( - "OVERCLOUD_CREDS is not set. Can't connect to Overcloud") - return [] + def addresses(self): + # Return: + # * return a list of the node's port addresses + + # TODO(Tzu-Mainn Chen): uncomment when possible + # ports = self.list_ports() + ports = test_data().ironicclient_ports.list()[:2] + + return [port.address for port in ports] -class TuskarNode(StringIdAPIResourceWrapper): - """Wrapper for the TuskarNode object returned by the dummy model.""" - - _attrs = ['id', 'nova_baremetal_node_id'] +class Resource(base.APIResourceWrapper): + _attrs = ('resource_name', 'resource_type', 'resource_status', + 'physical_resource_id') @classmethod - def get(cls, request, tuskar_node_id): - tuskar_node = cls(tuskarclient(request).nodes.get(tuskar_node_id)) - tuskar_node.request = request - return tuskar_node + def get(cls, request, stack, resource_name): + # Required: + # * stack, resource_name + # Return: + # * the matching Resource in the stack - @classmethod - def list(cls, request): - return [cls(n, request) for n in (tuskarclient(request).nodes.list())] + # TODO(Tzu-Mainn Chen): uncomment when possible + # resource = heatclient(request).resources.get( + # stack.id, + # resource_name) + resources = test_data().heatclient_resources.list() + resource = next((r for r in resources + if stack.id == r.stack_id + and resource_name == r.resource_name), + None) - def remove_from_rack(self, request): - rack = self.rack - baremetal_node_ids = [{'id': tuskar_node.nova_baremetal_node_id} - for tuskar_node in rack.list_tuskar_nodes - if tuskar_node.id != self.id] - Rack.update(request, rack.id, {'baremetal_nodes': baremetal_node_ids}) + return cls(resource) @cached_property - def rack(self): - if self.rack_id: - return Rack.get(self.request, self.rack_id) - else: - return None + def node(self): + # Return: + # * return resource's associated Node - @property - def rack_id(self): - try: - return unicode(self._apiresource.rack['id']) - except AttributeError: - return None + return next((n for n in Node.list + if self.physical_resource_id == n.instance_uuid), + None) @cached_property - def nova_baremetal_node(self): - if self.nova_baremetal_node_id: - return BaremetalNode.get(self.request, self.nova_baremetal_node_id) - else: - return None + def resource_category(self): + # Questions: + # * is a resource_type mapped directly to a ResourceCategory? + # * can we assume that the resource_type equals the category + # name? + # Return: + # * the ResourceCategory matching this resource - def nova_baremetal_node_attribute(self, attr_name): - key = "_%s" % attr_name - if not hasattr(self, key): - if self.nova_baremetal_node: - value = getattr(self.nova_baremetal_node, attr_name, None) - else: - value = None - setattr(self, key, value) - return getattr(self, key) + return ResourceCategory({'name': self.resource_type}) - @property - def service_host(self): - return self.nova_baremetal_node_attribute('service_host') - @property - def mac_address(self): - return self.nova_baremetal_node_attribute('mac_address') - - @property - def ip_address_other(self): - return self.nova_baremetal_node_attribute('ip_address_other') - - @property - def pm_address(self): - return self.nova_baremetal_node_attribute('pm_address') - - @property - def status(self): - return self.nova_baremetal_node_attribute('status') - - @property - def running_virtual_machines(self): - return self.nova_baremetal_node_attribute('running_virtual_machines') +class ResourceCategory(base.APIResourceWrapper): + _attrs = ('name') @cached_property - def list_flavors(self): - # FIXME: just a mock of used instances, add real values - used_instances = 0 + def image(self): + # Questions: + # * when a user uploads an image, how do we enforce + # that it matches the image name? + # Return: + # * the image name associated with the ResourceCategory - if not self.rack or not self.rack.resource_class: - return [] - resource_class = self.rack.resource_class + # TODO(Tzu-Mainn Chen): uncomment when possible + # return some-api-call-to-tuskarclient - added_flavors = tuskarclient( - self.request).flavors.list(resource_class.id) - flavors = [] - if added_flavors: - for flavor in added_flavors: - flavor_obj = Flavor(flavor) - #flavor_obj.max_vms = f.max_vms - - # FIXME just a mock of used instances, add real values - used_instances += 5 - flavor_obj.used_instances = used_instances - flavors.append(flavor_obj) - return flavors - - @property - # FIXME: just mock implementation, add proper one - def is_provisioned(self): - return (self.status != "unprovisioned" and self.rack) + return "image_name" @cached_property - def alerts(self): - return [] - - -class Rack(StringIdAPIResourceWrapper): - """Wrapper for the Rack object returned by the - dummy model. - """ - - _attrs = ['id', 'name', 'location', 'subnet', 'nodes', 'state', - 'capacities'] - - @classmethod - def create(cls, request, **kwargs): - baremetal_node_ids = kwargs.get('baremetal_nodes', []) - ## FIXME: set nodes here - rack = tuskarclient(request).racks.create( - name=kwargs['name'], - location=kwargs['location'], - subnet=kwargs['subnet'], - nodes=baremetal_node_ids, - resource_class={'id': kwargs['resource_class_id']}, - slots=0) - return cls(rack) - - @classmethod - def update(cls, request, rack_id, rack_kwargs): - rack_args = copy.copy(rack_kwargs) - # remove rack_id from kwargs (othervise it is duplicated) - rack_args.pop('rack_id', None) - ## FIXME: set nodes here - baremetal_node_ids = rack_args.pop('baremetal_nodes', None) - if baremetal_node_ids is not None: - rack_args['nodes'] = baremetal_node_ids - # correct data mapping for resource_class - if 'resource_class_id' in rack_args: - rack_args['resource_class'] = { - 'id': rack_args.pop('resource_class_id', None)} - - rack = tuskarclient(request).racks.update(rack_id, **rack_args) - return cls(rack) - - @classmethod - def list(cls, request, only_free_racks=False): - if only_free_racks: - return [Rack(r, request) for r in - tuskarclient(request).racks.list() if ( - getattr(r, 'resource_class', None) is None)] - else: - return [Rack(r, request) for r in - tuskarclient(request).racks.list()] - - @classmethod - def get(cls, request, rack_id): - rack = cls(tuskarclient(request).racks.get(rack_id)) - rack.request = request - return rack - - @classmethod - def delete(cls, request, rack_id): - tuskarclient(request).racks.delete(rack_id) - - @property - def tuskar_node_ids(self): - """List of unicode ids of tuskar nodes added to rack.""" - return [unicode(tuskar_node['id']) for tuskar_node in self.nodes] - - @cached_property - def list_tuskar_nodes(self): - return [TuskarNode.get(self.request, tuskar_node['id']) - for tuskar_node in self.nodes] - - @cached_property - def list_baremetal_nodes(self): - return [tuskar_node.nova_baremetal_node - for tuskar_node in self.list_tuskar_nodes] - - @property - def nodes_count(self): - return len(self.nodes) - - @property - def resource_class_id(self): - try: - return self._apiresource.resource_class['id'] - except AttributeError: - return None - - @cached_property - def resource_class(self): - if self.resource_class_id: - return ResourceClass.get(self.request, self.resource_class_id) - else: - return None - - @cached_property - def list_capacities(self): - return [Capacity(c) for c in self.capacities] - - @cached_property - def vm_capacity(self): - """Calculate Rack VM Capacity. - - Rack VM Capacity is maximum value from its Resource Class's Flavors - max_vms (considering flavor sizes are multiples). - """ - try: - value = max([flavor.max_vms for flavor in - self.resource_class.list_flavors]) - except Exception: - value = None - return Capacity({'name': "VM Capacity", 'value': value, 'unit': 'VMs'}) - - @cached_property - def alerts(self): - return [] - - @property - def aggregated_alerts(self): - # FIXME: for now return only list of nodes (particular alerts are not - # used) - return [tuskar_node for tuskar_node in self.list_tuskar_nodes - if tuskar_node.alerts] - - @cached_property - def list_flavors(self): - # FIXME just a mock of used instances, add real values - used_instances = 0 - - if not self.resource_class: - return [] - added_flavors = tuskarclient( - self.request).flavors.list(self.resource_class_id) - flavors = [] - for flavor in added_flavors: - flavor_obj = Flavor(flavor) - #flavor_obj.max_vms = f.max_vms - - # FIXME just a mock of used instances, add real values - used_instances += 2 - flavor_obj.used_instances = used_instances - flavors.append(flavor_obj) - return flavors - - @property - def all_used_instances(self): - return [flavor.used_instances for flavor in self.list_flavors] - - @property - def total_instances(self): - # FIXME just mock implementation, add proper one - return sum(self.all_used_instances) - - @property - def remaining_capacity(self): - # FIXME just mock implementation, add proper one - return 100 - self.total_instances - - @property - def is_provisioned(self): - return (self.state == 'active') or (self.state == 'error') - - @property - def is_provisioning(self): - return (self.state == 'provisioning') - - @classmethod - def provision_all(cls, request): - tuskarclient(request).data_centers.provision_all() - - -class ResourceClass(StringIdAPIResourceWrapper): - """Wrapper for the ResourceClass object returned by the - dummy model. - """ - _attrs = ['id', 'name', 'service_type', 'image_id', 'racks'] - - @classmethod - def get(cls, request, resource_class_id): - rc = cls(tuskarclient(request).resource_classes.get(resource_class_id)) - rc.request = request - return rc - - @classmethod - def create(self, request, name, service_type, image_id, flavors): - return ResourceClass( - tuskarclient(request).resource_classes.create( - name=name, - service_type=service_type, - image_id=image_id, - flavors=flavors)) - - @classmethod - def list(cls, request): - return [cls(rc, request) for rc in ( - tuskarclient(request).resource_classes.list())] - - @classmethod - def update(cls, request, resource_class_id, name, service_type, image_id, - flavors): - resource_class = cls(tuskarclient(request).resource_classes.update( - resource_class_id, - name=name, - service_type=service_type, - image_id=image_id, - flavors=flavors)) - - ## FIXME: flavors have to be updated separately, seems less than ideal - for flavor_id in resource_class.flavors_ids: - Flavor.delete(request, resource_class_id=resource_class.id, - flavor_id=flavor_id) - for flavor in flavors: - Flavor.create(request, - resource_class_id=resource_class.id, - **flavor) - - return resource_class - - @property - def deletable(self): - return (len(self.racks) <= 0) - - @classmethod - def delete(cls, request, resource_class_id): - tuskarclient(request).resource_classes.delete(resource_class_id) - - @property - def racks_ids(self): - """List of unicode ids of racks added to resource class.""" - return [unicode(rack['id']) for rack in self.racks] - - @cached_property - def list_racks(self): - """List of racks added to ResourceClass.""" - return [Rack.get(self.request, rid) for rid in self.racks_ids] - - def set_racks(self, request, racks_ids): - # FIXME: there is a bug now in tuskar, we have to remove all racks at - # first and then add new ones: - # https://github.com/tuskar/tuskar/issues/37 - tuskarclient(request).resource_classes.update(self.id, racks=[]) - racks = [{'id': rid} for rid in racks_ids] - tuskarclient(request).resource_classes.update(self.id, racks=racks) - - @property - def racks_count(self): - return len(self.racks) - - @cached_property - def all_racks(self): - """List all racks suitable for the add/remove dialog. - - List of racks added to ResourceClass + list of free racks, - meaning racks that don't belong to any ResourceClass. - """ - return [rack for rack in Rack.list(self.request) - if rack.resource_class_id is None or - str(rack.resource_class_id) == self.id] - - @cached_property - def tuskar_nodes(self): - return [tuskar_node for tuskar_node in TuskarNode.list(self.request) - if tuskar_node.rack_id in self.racks_ids] - - @property - def nodes_count(self): - return len(self.tuskar_nodes) - - @property - def flavors_ids(self): - """List of unicode ids of flavors added to resource class.""" - return [unicode(flavor.id) for flavor in self.list_flavors] - - @cached_property - def list_flavors(self): - # FIXME just a mock of used instances, add real values - used_instances = 0 - - added_flavors = tuskarclient(self.request).flavors.list(self.id) - flavors = [] - for flavor in added_flavors: - flavor_obj = Flavor(flavor, self.request) - #flavor_obj.max_vms = f.max_vms - - # FIXME just a mock of used instances, add real values - used_instances += 5 - flavor_obj.used_instances = used_instances - flavors.append(flavor_obj) - return flavors - - @property - def all_used_instances(self): - return [flavor.used_instances for flavor in self.list_flavors] - - @property - def total_instances(self): - # FIXME just mock implementation, add proper one - return sum(self.all_used_instances) - - @property - def remaining_capacity(self): - # FIXME just mock implementation, add proper one - return 100 - self.total_instances - - @cached_property - def capacities(self): - """Aggregates Rack capacities values.""" - - def add_capacities(c1, c2): - return [Capacity({ - 'name': a.name, - 'value': int(a.value) + int(b.value), - 'unit': a.unit, - }) for a, b in zip(c1, c2)] - - capacities = [rack.list_capacities for rack in self.list_racks] - return reduce(add_capacities, capacities) - - @cached_property - def vm_capacity(self): - """Calculate Class VM Capacity. - - Resource Class VM Capacity is maximum value from its Flavors max_vms - (considering flavor sizes are multiples), multipled by number of Racks - in Resource Class. - """ - try: - value = self.racks_count * max([flavor.max_vms - for flavor in self.list_flavors]) - except Exception: - value = _("Unable to retrieve vm capacity") - return Capacity({ - 'name': _("VM Capacity"), - 'value': value, - 'unit': _('VMs'), - }) - - @property - def aggregated_alerts(self): - # FIXME: for now return only list of racks (particular alerts are not - # used) - return [rack - for rack in self.list_racks - if rack.alerts + rack.aggregated_alerts] - - @property - def has_provisioned_rack(self): - return any([rack.is_provisioned for rack in self.list_racks]) - - -class Flavor(StringIdAPIResourceWrapper): - """Wrapper for the Flavor object returned by Tuskar. - """ - _attrs = ['id', 'name', 'max_vms'] - - @classmethod - def get(cls, request, resource_class_id, flavor_id): - flavor = cls(tuskarclient(request).flavors.get(resource_class_id, - flavor_id)) - flavor.request = request - return flavor - - @classmethod - def create(cls, request, **kwargs): - return cls(tuskarclient(request).flavors.create( - kwargs['resource_class_id'], - name=kwargs['name'], - max_vms=kwargs['max_vms'], - capacities=kwargs['capacities'])) - - @classmethod - def delete(cls, request, **kwargs): - tuskarclient(request).flavors.delete( - kwargs['resource_class_id'], - kwargs['flavor_id']) - - @cached_property - def capacities(self): - # FIXME: should we distinguish between tuskar capacities and our - # internal capacities? - CapacityStruct = collections.namedtuple( - 'CapacityStruct', 'name value unit') - return [Capacity(CapacityStruct(c['name'], c['value'], c['unit'])) - for c in self._apiresource.capacities] - - def capacity(self, capacity_name): - key = "_%s" % capacity_name - if not hasattr(self, key): - try: - capacity = [c for c in self.capacities if ( - c.name == capacity_name)][0] - except Exception: - # FIXME: test this - capacity = Capacity( - name=capacity_name, - value=_('Unable to retrieve ' - '(Is the flavor configured properly?)'), - unit='') - setattr(self, key, capacity) - return getattr(self, key) - - @property - def cpu(self): - return self.capacity('cpu') - - @property - def memory(self): - return self.capacity('memory') - - @property - def storage(self): - return self.capacity('storage') - - @property - def ephemeral_disk(self): - return self.capacity('ephemeral_disk') - - @property - def swap_disk(self): - return self.capacity('swap_disk') - - @property - def running_virtual_machines(self): - # FIXME: arbitrary number - return random.randint(0, int(self.cpu.value)) - - # defines a random average of capacity - API should probably be able to - # determine average of capacity based on capacity value and obejct_id - def vms_over_time(self, start_time, end_time): - values = [] - current_time = start_time - while current_time <= end_time: - values.append( - {'date': current_time, - 'value': random.randint(0, self.running_virtual_machines)}) - current_time += datetime.timedelta(hours=1) - - return values + def resources(self, stack): + # Questions: + # * can we assume that the resource_type equals the + # category name? + # Required: + # * stack + # Return: + # * the resources within the stack that match the + # resource category + + return [r for r in stack.resources if r.resource_type == self.name] diff --git a/tuskar_ui/infrastructure/resources/free/tests.py b/tuskar_ui/infrastructure/resources/free/tests.py index 9e15cd86f..d8afa60a5 100644 --- a/tuskar_ui/infrastructure/resources/free/tests.py +++ b/tuskar_ui/infrastructure/resources/free/tests.py @@ -16,6 +16,7 @@ from django.core import urlresolvers from mock import patch # noqa +from tuskar_ui import api from tuskar_ui.test import helpers as test @@ -30,9 +31,10 @@ class FreeNodesTests(test.BaseAdminViewTests): super(FreeNodesTests, self).setUp() def test_index(self): - free_nodes = self.baremetal_nodes.list() + free_nodes = [api.Node(node) + for node in self.ironicclient_nodes.list()] - with patch('tuskar_ui.api.BaremetalNode', **{ + with patch('tuskar_ui.api.Node', **{ 'spec_set': ['list'], # Only allow these attributes 'list.return_value': free_nodes, }) as mock: @@ -46,7 +48,7 @@ class FreeNodesTests(test.BaseAdminViewTests): free_nodes) def test_index_nodes_list_exception(self): - with patch('tuskar_ui.api.BaremetalNode', **{ + with patch('tuskar_ui.api.Node', **{ 'spec_set': ['list'], 'list.side_effect': self.exceptions.tuskar, }) as mock: diff --git a/tuskar_ui/infrastructure/resources/free/views.py b/tuskar_ui/infrastructure/resources/free/views.py index a32494601..7382c03b6 100644 --- a/tuskar_ui/infrastructure/resources/free/views.py +++ b/tuskar_ui/infrastructure/resources/free/views.py @@ -28,9 +28,7 @@ class IndexView(horizon_tables.DataTableView): def get_data(self): try: - # TODO(Jiri Tomasek): needs update when filtering by node type is - # available - free_nodes = tuskar.BaremetalNode.list(self.request) + free_nodes = tuskar.Node.list(self.request, associated=False) except Exception: free_nodes = [] redirect = urlresolvers.reverse( diff --git a/tuskar_ui/infrastructure/resources/resource/tests.py b/tuskar_ui/infrastructure/resources/resource/tests.py index 16d042ad8..879cf4c23 100644 --- a/tuskar_ui/infrastructure/resources/resource/tests.py +++ b/tuskar_ui/infrastructure/resources/resource/tests.py @@ -16,6 +16,7 @@ from django.core import urlresolvers from mock import patch # noqa +from tuskar_ui import api from tuskar_ui.test import helpers as test @@ -30,9 +31,10 @@ class ResourceNodesTests(test.BaseAdminViewTests): super(ResourceNodesTests, self).setUp() def test_index(self): - resource_nodes = self.baremetal_nodes.list() + resource_nodes = [api.Node(node) + for node in self.ironicclient_nodes.list()] - with patch('tuskar_ui.api.BaremetalNode', **{ + with patch('tuskar_ui.api.Node', **{ 'spec_set': ['list'], # Only allow these attributes 'list.return_value': resource_nodes, }) as mock: @@ -47,7 +49,7 @@ class ResourceNodesTests(test.BaseAdminViewTests): resource_nodes) def test_index_nodes_list_exception(self): - with patch('tuskar_ui.api.BaremetalNode', **{ + with patch('tuskar_ui.api.Node', **{ 'spec_set': ['list'], 'list.side_effect': self.exceptions.tuskar, }) as mock: diff --git a/tuskar_ui/infrastructure/resources/resource/views.py b/tuskar_ui/infrastructure/resources/resource/views.py index a23789c04..c825613ff 100644 --- a/tuskar_ui/infrastructure/resources/resource/views.py +++ b/tuskar_ui/infrastructure/resources/resource/views.py @@ -28,9 +28,7 @@ class IndexView(horizon_tables.DataTableView): def get_data(self): try: - # TODO(Jiri Tomasek): needs update when filtering by node type is - # available - resource_nodes = tuskar.BaremetalNode.list(self.request) + resource_nodes = tuskar.Node.list(self.request, associated=True) except Exception: resource_nodes = [] redirect = urlresolvers.reverse( diff --git a/tuskar_ui/infrastructure/resources/tables.py b/tuskar_ui/infrastructure/resources/tables.py index e94eb279e..67e6950e8 100644 --- a/tuskar_ui/infrastructure/resources/tables.py +++ b/tuskar_ui/infrastructure/resources/tables.py @@ -12,21 +12,36 @@ # License for the specific language governing permissions and limitations # under the License. +from django.template import defaultfilters as filters from django.utils.translation import ugettext_lazy as _ # noqa from horizon import tables class NodesTable(tables.DataTable): - service_host = tables.Column( - "service_host", - verbose_name=_("Service Host"), - link=("horizon:infrastructure:resources.management:detail")) - mac_address = tables.Column("mac_address", verbose_name=_("MAC Address")) - pm_address = tables.Column("pm_address", - verbose_name=_("Management Address")) - status = tables.Column("status", verbose_name=_("Status")) - usage = tables.Column("usage", verbose_name=_("Usage")) + + uuid = tables.Column("uuid", + verbose_name=_("UUID")) + mac_addresses = tables.Column("addresses", + verbose_name=_("MAC Addresses"), + wrap_list=True, + filters=(filters.unordered_list,)) + ipmi_address = tables.Column(lambda node: node.driver_info['ipmi_address'], + verbose_name=_("IPMI Address")) + cpu = tables.Column(lambda node: node.properties['cpu'], + verbose_name=_("CPU")) + ram = tables.Column(lambda node: node.properties['ram'], + verbose_name=_("RAM (GB)")) + local_disk = tables.Column(lambda node: node.properties['local_disk'], + verbose_name=_("Local Disk (TB)")) + status = tables.Column("power_state", + verbose_name=_("Status"), + status=True, + status_choices=( + ('on', True), + ('off', False), + ('rebooting', None) + )) class Meta: name = "nodes_table" @@ -34,5 +49,8 @@ class NodesTable(tables.DataTable): table_actions = () row_actions = () + def get_object_id(self, datum): + return datum.uuid + def get_object_display(self, datum): - return datum.service_host + return datum.uuid diff --git a/tuskar_ui/test/api_tests/tuskar_tests.py b/tuskar_ui/test/api_tests/tuskar_tests.py index c3af0db97..4846df22a 100644 --- a/tuskar_ui/test/api_tests/tuskar_tests.py +++ b/tuskar_ui/test/api_tests/tuskar_tests.py @@ -14,735 +14,14 @@ from __future__ import absolute_import -from novaclient.v1_1.contrib import baremetal - -from tuskar_ui import api +# TODO(Tzu-Mainn Chen): uncomment once we add API tests +#from tuskar_ui import api from tuskar_ui.test import helpers as test -class TuskarApiTests(test.APITestCase): +class TuskarAPITests(test.APITestCase): - def test_baremetal_node_create(self): - baremetal_node = self.baremetalclient_nodes.first() + def setUp(self): + super(TuskarAPITests, self).setUp() - self.mox.StubOutWithMock(baremetal.BareMetalNodeManager, 'create') - baremetal.BareMetalNodeManager.create( - 'node', - 1, - 1024, - 10, - 'aa:bb:cc:dd:ee:ff', - '0.0.0.0', - 'user', - 'password', - 0).AndReturn(baremetal_node) - self.mox.ReplayAll() - - ret_val = api.BaremetalNode.create( - self.request, - service_host='node', - cpus=1, - memory_mb=1024, - local_gb=10, - prov_mac_address='aa:bb:cc:dd:ee:ff', - pm_address='0.0.0.0', - pm_user='user', - pm_password='password', - terminal_port=0) - self.assertIsInstance(ret_val, api.BaremetalNode) - - def test_baremetal_node_create_with_empty_pm(self): - """Make sure that when pm_address, pm_user and terminal_port are not - provided (empty), their values are set to None, as this is required by - the baremetal VM. - """ - baremetal_node = self.baremetalclient_nodes.first() - - self.mox.StubOutWithMock(baremetal.BareMetalNodeManager, 'create') - baremetal.BareMetalNodeManager.create( - 'node', 1, 1024, 10, - 'AA:BB:CC:DD:EE:FF', - None, None, '', None).AndReturn(baremetal_node) - self.mox.ReplayAll() - - ret_val = api.BaremetalNode.create( - self.request, service_host='node', cpus=1, - memory_mb=1024, local_gb=10, prov_mac_address='AA:BB:CC:DD:EE:FF', - pm_address='', pm_user='', pm_password='', terminal_port='') - self.assertIsInstance(ret_val, api.BaremetalNode) - - def test_baremetal_node_get(self): - baremetal_node = self.baremetalclient_nodes.first() - server = self.servers.first() - - self.mox.StubOutWithMock(baremetal.BareMetalNodeManager, 'get') - baremetal.BareMetalNodeManager.get( - baremetal_node.id).AndReturn(baremetal_node) - - novaclient = self.stub_novaclient() - novaclient.servers = self.mox.CreateMockAnything() - novaclient.servers.get(baremetal_node.instance_uuid).AndReturn(server) - - self.mox.ReplayAll() - - ret_val = api.BaremetalNode.get(self.request, baremetal_node.id) - self.assertIsInstance(ret_val, api.BaremetalNode) - - def test_baremetal_node_list(self): - baremetal_nodes = self.baremetalclient_nodes_all.list() - - self.mox.StubOutWithMock(baremetal.BareMetalNodeManager, 'list') - baremetal.BareMetalNodeManager.list().AndReturn(baremetal_nodes) - self.mox.ReplayAll() - - ret_val = api.BaremetalNode.list(self.request) - for baremetal_node in ret_val: - self.assertIsInstance(baremetal_node, api.BaremetalNode) - - def test_baremetal_node_list_unracked(self): - tuskarclient_nodes = self.tuskarclient_nodes.list() - baremetal_nodes = self.baremetalclient_nodes_all.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.nodes = self.mox.CreateMockAnything() - tuskarclient.nodes.list().AndReturn(tuskarclient_nodes) - - self.mox.StubOutWithMock(baremetal.BareMetalNodeManager, 'list') - baremetal.BareMetalNodeManager.list().AndReturn(baremetal_nodes) - - self.mox.ReplayAll() - - ret_val = api.BaremetalNode.list_unracked(self.request) - for baremetal_node in ret_val: - self.assertIsInstance(baremetal_node, api.BaremetalNode) - self.assertEquals(1, len(ret_val)) - - def test_baremetal_node_running_instances(self): - baremetal_node = self.baremetal_nodes.first() - - self.assertEquals(4, baremetal_node.running_instances) - - def test_baremetal_node_remaining_capacity(self): - baremetal_node = self.baremetal_nodes.first() - - self.assertEquals(96, baremetal_node.remaining_capacity) - - def test_node_get(self): - tuskar_node = self.tuskarclient_nodes.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.nodes = self.mox.CreateMockAnything() - tuskarclient.nodes.get(tuskar_node.id).AndReturn(tuskar_node) - self.mox.ReplayAll() - - ret_val = api.TuskarNode.get(self.request, tuskar_node.id) - self.assertIsInstance(ret_val, api.TuskarNode) - - def test_node_list(self): - tuskarclient_nodes = self.tuskarclient_nodes.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.nodes = self.mox.CreateMockAnything() - tuskarclient.nodes.list().AndReturn(tuskarclient_nodes) - self.mox.ReplayAll() - - ret_val = api.TuskarNode.list(self.request) - for tuskar_node in ret_val: - self.assertIsInstance(tuskar_node, api.TuskarNode) - - def test_node_remove_from_rack(self): - tuskar_nodes = self.tuskar_nodes.list() - tuskar_node = tuskar_nodes[0] - rack = self.tuskar_racks.first() - - self.mox.StubOutWithMock(api.Rack, 'get') - self.mox.StubOutWithMock(api.Rack, 'update') - self.mox.StubOutWithMock(api.Rack, 'list_tuskar_nodes') - - api.Rack.get(self.request, rack.id).AndReturn(rack) - api.Rack.list_tuskar_nodes = tuskar_nodes - api.Rack.update(self.request, rack.id, { - 'baremetal_nodes': [{'id': t_node.nova_baremetal_node_id} - for t_node in tuskar_nodes - if t_node.id != tuskar_node.id], - }).AndReturn(rack) - self.mox.ReplayAll() - - tuskar_node.request = self.request - rack.request = self.request - tuskar_node.remove_from_rack(self.request) - - def test_node_rack(self): - tuskar_node = self.tuskar_nodes.first() - rack = self.tuskarclient_racks.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.get(rack.id).AndReturn(rack) - self.mox.ReplayAll() - - tuskar_node.request = self.request - self.assertIsInstance(tuskar_node.rack, api.Rack) - self.assertEquals('1', tuskar_node.rack_id) - - def test_node_nova_baremetal_node(self): - tuskar_node = self.tuskar_nodes.first() - bm_node = self.baremetalclient_nodes.first() - - self.mox.StubOutWithMock(baremetal.BareMetalNodeManager, 'get') - baremetal.BareMetalNodeManager.get( - tuskar_node.nova_baremetal_node_id).AndReturn(bm_node) - - novaclient = self.stub_novaclient() - novaclient.servers = self.mox.CreateMockAnything() - novaclient.servers.list(True, - {'all_tenants': True, - 'limit': 21}).AndReturn([]) - self.mox.ReplayAll() - - tuskar_node.request = self.request - baremetal_node = tuskar_node.nova_baremetal_node - self.assertIsInstance(baremetal_node, api.BaremetalNode) - self.assertEquals('11', baremetal_node.id) - - def test_node_flavors(self): - tuskar_node = self.tuskar_nodes.first() - rack = self.tuskarclient_racks.first() - rc = self.tuskarclient_resource_classes.first() - flavors = self.tuskarclient_flavors.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.get(rack.id).AndReturn(rack) - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.get(rc.id).AndReturn(rc) - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.list(rc.id).AndReturn(flavors) - self.mox.ReplayAll() - - tuskar_node.request = self.request - ret_val = tuskar_node.list_flavors - for flavor in ret_val: - self.assertIsInstance(flavor, api.Flavor) - self.assertEquals(2, len(ret_val)) - - def test_node_is_provisioned(self): - tuskar_node = self.tuskar_nodes.first() - tuskar_node.request = self.request - bm_node = self.baremetalclient_nodes.first() - - self.mox.StubOutWithMock(baremetal.BareMetalNodeManager, 'get') - baremetal.BareMetalNodeManager.get( - tuskar_node.nova_baremetal_node_id).AndReturn(bm_node) - - novaclient = self.stub_novaclient() - novaclient.servers = self.mox.CreateMockAnything() - novaclient.servers.list(True, - {'all_tenants': True, - 'limit': 21}).AndReturn([]) - self.mox.ReplayAll() - - self.assertFalse(tuskar_node.is_provisioned) - - def test_node_alerts(self): - tuskar_node = self.tuskar_nodes.first() - - self.assertEquals([], tuskar_node.alerts) - - def test_resource_class_list(self): - rcs = self.tuskarclient_resource_classes.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.list().AndReturn(rcs) - self.mox.ReplayAll() - - ret_val = api.ResourceClass.list(self.request) - for rc in ret_val: - self.assertIsInstance(rc, api.ResourceClass) - - def test_resource_class_get(self): - rc = self.tuskarclient_resource_classes.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.get(rc.id).AndReturn(rc) - self.mox.ReplayAll() - - ret_val = api.ResourceClass.get(self.request, rc.id) - self.assertIsInstance(ret_val, api.ResourceClass) - - def test_resource_class_create(self): - rc = self.tuskarclient_resource_classes.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.create(name='rclass1', - service_type='compute', - image_id=None, - flavors=[]).AndReturn(rc) - self.mox.ReplayAll() - - ret_val = api.ResourceClass.create(self.request, - name='rclass1', - service_type='compute', - image_id=None, - flavors=[]) - self.assertIsInstance(ret_val, api.ResourceClass) - - def test_resource_class_update(self): - rc = self.tuskarclient_resource_classes.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.resource_classes.update(rc.id, - name='rclass1', - service_type='compute', - image_id=None, - flavors=[]).AndReturn(rc) - tuskarclient.flavors.list(rc.id).AndReturn([]) - self.mox.ReplayAll() - - ret_val = api.ResourceClass.update(self.request, rc.id, - name='rclass1', - service_type='compute', - image_id=None, - flavors=[]) - self.assertIsInstance(ret_val, api.ResourceClass) - - def test_resource_class_delete(self): - rc = self.tuskarclient_resource_classes.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.delete(rc.id) - self.mox.ReplayAll() - - api.ResourceClass.delete(self.request, rc.id) - - def test_resource_class_deletable(self): - rc = self.tuskar_resource_classes.first() - - self.assertFalse(rc.deletable) - - def test_resource_class_racks(self): - rc = self.tuskar_resource_classes.first() - racks = self.tuskarclient_racks.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.get('1').AndReturn(racks[0]) - tuskarclient.racks.get('2').AndReturn(racks[1]) - self.mox.ReplayAll() - - for rack in rc.list_racks: - self.assertIsInstance(rack, api.Rack) - self.assertEquals(2, rc.racks_count) - - def test_resource_class_all_racks(self): - rc = self.tuskar_resource_classes.first() - racks = self.tuskarclient_racks.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.list().AndReturn(racks) - self.mox.ReplayAll() - - all_racks = rc.all_racks - for rack in all_racks: - self.assertIsInstance(rack, api.Rack) - self.assertEquals(3, len(all_racks)) - - def test_resource_class_racks_set(self): - rc = self.tuskar_resource_classes.first() - racks = self.tuskarclient_racks.list() - rack_ids = [rack.id for rack in racks] - rack_data = [{'id': rack_id} for rack_id in rack_ids] - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.update(rc.id, - racks=[]) - tuskarclient.resource_classes.update(rc.id, - racks=rack_data) - self.mox.ReplayAll() - - rc.set_racks(self.request, rack_ids) - - def test_resource_class_nodes(self): - rc = self.tuskar_resource_classes.first() - tuskar_nodes = self.tuskarclient_nodes.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.nodes = self.mox.CreateMockAnything() - tuskarclient.nodes.list().AndReturn(tuskar_nodes) - self.mox.ReplayAll() - - rc.request = self.request - for tuskar_node in rc.tuskar_nodes: - self.assertIsInstance(tuskar_node, api.TuskarNode) - self.assertEquals(4, rc.nodes_count) - - def test_resource_class_flavors(self): - rc = self.tuskar_resource_classes.first() - flavors = self.tuskarclient_flavors.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.list(rc.id).AndReturn(flavors) - self.mox.ReplayAll() - - for f in rc.list_flavors: - self.assertIsInstance(f, api.Flavor) - self.assertEquals(2, len(rc.flavors_ids)) - - def test_resource_class_capacities(self): - rc = self.tuskar_resource_classes.first() - racks = self.tuskarclient_racks.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.get('1').AndReturn(racks[0]) - tuskarclient.racks.get('2').AndReturn(racks[1]) - self.mox.ReplayAll() - - for capacity in rc.capacities: - self.assertIsInstance(capacity, api.Capacity) - self.assertEquals(2, len(rc.capacities)) - - def test_resource_class_total_instances(self): - rc = self.tuskar_resource_classes.first() - flavors = self.tuskarclient_flavors.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.list(rc.id).AndReturn(flavors) - self.mox.ReplayAll() - - self.assertEquals(15, rc.total_instances) - - def test_resource_class_remaining_capacity(self): - rc = self.tuskar_resource_classes.first() - flavors = self.tuskarclient_flavors.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.list(rc.id).AndReturn(flavors) - self.mox.ReplayAll() - - self.assertEquals(85, rc.remaining_capacity) - - def test_resource_class_vm_capacity(self): - rc = self.tuskar_resource_classes.first() - flavors = self.tuskarclient_flavors.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.list(rc.id).AndReturn(flavors) - self.mox.ReplayAll() - - vm_capacity = rc.vm_capacity - self.assertIsInstance(vm_capacity, api.Capacity) - self.assertEquals(200, vm_capacity.value) - - def test_resource_class_has_provisioned_rack(self): - rc1 = self.tuskar_resource_classes.list()[0] - rc2 = self.tuskar_resource_classes.list()[1] - racks = self.tuskarclient_racks.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.get('1').AndReturn(racks[0]) - tuskarclient.racks.get('2').AndReturn(racks[1]) - self.mox.ReplayAll() - - self.assertTrue(rc1.has_provisioned_rack) - self.assertFalse(rc2.has_provisioned_rack) - - def test_resource_class_aggregated_alerts(self): - rc = self.tuskar_resource_classes.list()[0] - rc.request = self.request - tuskar_nodes = self.tuskarclient_nodes.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - racks = self.tuskarclient_racks.list() - tuskarclient.racks.get('1').AndReturn(racks[0]) - tuskarclient.racks.get('2').AndReturn(racks[1]) - - tuskarclient.nodes = self.mox.CreateMockAnything() - for tuskar_node in tuskar_nodes: - tuskarclient.nodes.get(tuskar_node.id).AndReturn(tuskar_node) - - self.mox.ReplayAll() - - for rack in rc.aggregated_alerts: - self.assertIsInstance(rack, api.Rack) - self.assertEquals(0, len(rc.aggregated_alerts)) - - def test_rack_list(self): - racks = self.tuskarclient_racks.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.list().AndReturn(racks) - self.mox.ReplayAll() - - ret_val = api.Rack.list(self.request) - for rack in ret_val: - self.assertIsInstance(rack, api.Rack) - - def test_rack_get(self): - rack = self.tuskarclient_racks.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.get(rack.id).AndReturn(rack) - self.mox.ReplayAll() - - ret_val = api.Rack.get(self.request, rack.id) - self.assertIsInstance(ret_val, api.Rack) - - def test_rack_create(self): - rack = self.tuskarclient_racks.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.create(name='rack1', - location='location', - subnet='192.168.1.0/24', - nodes=[], - resource_class={'id': 1}, - slots=0).AndReturn(rack) - self.mox.ReplayAll() - - ret_val = api.Rack.create(request=self.request, - name='rack1', - resource_class_id=1, - location='location', - subnet='192.168.1.0/24') - self.assertIsInstance(ret_val, api.Rack) - - def test_rack_update(self): - rack = self.tuskarclient_racks.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.update(rack.id, - name='rack1').AndReturn(rack) - self.mox.ReplayAll() - - ret_val = api.Rack.update(self.request, - rack.id, - {'name': 'rack1'}) - self.assertIsInstance(ret_val, api.Rack) - - def test_rack_delete(self): - rack = self.tuskarclient_racks.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.racks = self.mox.CreateMockAnything() - tuskarclient.racks.delete(rack.id) - self.mox.ReplayAll() - - api.Rack.delete(self.request, rack.id) - - def test_rack_nodes(self): - rack = self.tuskar_racks.first() - tuskar_nodes = self.tuskarclient_nodes.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.nodes = self.mox.CreateMockAnything() - for tuskar_node in tuskar_nodes: - tuskarclient.nodes.get(tuskar_node.id).AndReturn(tuskar_node) - self.mox.ReplayAll() - - rack.request = self.request - for tuskar_node in rack.list_tuskar_nodes: - self.assertIsInstance(tuskar_node, api.TuskarNode) - self.assertEquals(4, len(rack.tuskar_node_ids)) - self.assertEquals(4, rack.nodes_count) - - def test_rack_resource_class(self): - rc = self.tuskarclient_resource_classes.first() - rack = self.tuskar_racks.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.get(rc.id).AndReturn(rc) - self.mox.ReplayAll() - - self.assertIsInstance(rack.resource_class, api.ResourceClass) - self.assertEquals(rack.resource_class_id, '1') - - def test_rack_capacities(self): - rack = self.tuskar_racks.first() - - for capacity in rack.list_capacities: - self.assertIsInstance(capacity, api.Capacity) - self.assertEquals(2, len(rack.capacities)) - - def test_rack_vm_capacity(self): - rack = self.tuskar_racks.first() - rc = self.tuskarclient_resource_classes.first() - flavors = self.tuskarclient_flavors.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.get(rc.id).AndReturn(rc) - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.list(rc.id).AndReturn(flavors) - self.mox.ReplayAll() - - vm_capacity = rack.vm_capacity - self.assertIsInstance(vm_capacity, api.Capacity) - self.assertEquals(100, vm_capacity.value) - - def test_rack_flavors(self): - rack = self.tuskar_racks.first() - rc = self.tuskarclient_resource_classes.first() - flavors = self.tuskarclient_flavors.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.get(rc.id).AndReturn(rc) - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.list(rc.id).AndReturn(flavors) - self.mox.ReplayAll() - - rack_flavors = rack.list_flavors - for f in rack_flavors: - self.assertIsInstance(f, api.Flavor) - self.assertEquals(2, len(rack_flavors)) - - def test_rack_total_instances(self): - rack = self.tuskar_racks.first() - rc = self.tuskarclient_resource_classes.first() - flavors = self.tuskarclient_flavors.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.get(rc.id).AndReturn(rc) - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.list(rc.id).AndReturn(flavors) - self.mox.ReplayAll() - - self.assertEquals(6, rack.total_instances) - - def test_rack_remaining_capacity(self): - rack = self.tuskar_racks.first() - rc = self.tuskarclient_resource_classes.first() - flavors = self.tuskarclient_flavors.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.resource_classes = self.mox.CreateMockAnything() - tuskarclient.resource_classes.get(rc.id).AndReturn(rc) - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.list(rc.id).AndReturn(flavors) - self.mox.ReplayAll() - - self.assertEquals(94, rack.remaining_capacity) - - def test_rack_is_provisioned(self): - rack1 = self.tuskar_racks.list()[0] - rack2 = self.tuskar_racks.list()[1] - - self.assertTrue(rack1.is_provisioned) - self.assertFalse(rack2.is_provisioned) - - def test_rack_is_provisioning(self): - rack1 = self.tuskar_racks.list()[0] - rack2 = self.tuskar_racks.list()[1] - - self.assertFalse(rack1.is_provisioning) - self.assertTrue(rack2.is_provisioning) - - def test_rack_provision(self): - tuskarclient = self.stub_tuskarclient() - tuskarclient.data_centers = self.mox.CreateMockAnything() - tuskarclient.data_centers.provision_all() - self.mox.ReplayAll() - - api.Rack.provision_all(self.request) - - def test_rack_aggregated_alerts(self): - rack = self.tuskar_racks.first() - rack.request = self.request - tuskar_nodes = self.tuskarclient_nodes.list() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.nodes = self.mox.CreateMockAnything() - for tuskar_node in tuskar_nodes: - tuskarclient.nodes.get(tuskar_node.id).AndReturn(tuskar_node) - self.mox.ReplayAll() - - for tuskar_node in rack.aggregated_alerts: - self.assertIsInstance(tuskar_node, api.TuskarNode) - self.assertEquals(0, len(rack.aggregated_alerts)) - - def test_flavor_create(self): - flavor = self.tuskarclient_flavors.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.create(1, - name='nano', - max_vms=100, - capacities=[]).AndReturn(flavor) - self.mox.ReplayAll() - - ret_val = api.Flavor.create(self.request, - resource_class_id=1, - name='nano', - max_vms=100, - capacities=[]) - self.assertIsInstance(ret_val, api.Flavor) - - def test_flavor_delete(self): - rc = self.tuskarclient_resource_classes.first() - flavor = self.tuskarclient_flavors.first() - - tuskarclient = self.stub_tuskarclient() - tuskarclient.flavors = self.mox.CreateMockAnything() - tuskarclient.flavors.delete(rc.id, flavor.id) - self.mox.ReplayAll() - - api.Flavor.delete(self.request, - resource_class_id=rc.id, - flavor_id=flavor.id) - - def test_flavor_cpu(self): - flavor = self.tuskar_flavors.first() - - cpu = flavor.cpu - self.assertIsInstance(cpu, api.Capacity) - self.assertEquals(64, cpu.value) - - def test_flavor_memory(self): - flavor = self.tuskar_flavors.first() - - memory = flavor.memory - self.assertIsInstance(memory, api.Capacity) - self.assertEquals(1024, memory.value) - - def test_flavor_storage(self): - flavor = self.tuskar_flavors.first() - - storage = flavor.storage - self.assertIsInstance(storage, api.Capacity) - self.assertEquals(1, storage.value) - - def test_flavor_ephemeral_disk(self): - flavor = self.tuskar_flavors.first() - - ephemeral_disk = flavor.ephemeral_disk - self.assertIsInstance(ephemeral_disk, api.Capacity) - self.assertEquals(0, ephemeral_disk.value) - - def test_flavor_swap_disk(self): - flavor = self.tuskar_flavors.first() - - swap_disk = flavor.swap_disk - self.assertIsInstance(swap_disk, api.Capacity) - self.assertEquals(2, swap_disk.value) + # to be written! diff --git a/tuskar_ui/test/helpers.py b/tuskar_ui/test/helpers.py index c3fb5d35e..216a67e36 100644 --- a/tuskar_ui/test/helpers.py +++ b/tuskar_ui/test/helpers.py @@ -17,7 +17,6 @@ import os from django.core.handlers import wsgi from django.utils import unittest -from novaclient.v1_1.contrib import baremetal from tuskarclient.v1 import client as tuskar_client from openstack_dashboard.test import helpers as openstack_dashboard_helpers @@ -82,25 +81,16 @@ class APITestCase(openstack_dashboard_helpers.APITestCase): # Store the original clients self._original_tuskarclient = tuskar_api.tuskarclient - self._original_baremetalclient = tuskar_api.baremetalclient # Replace the clients with our stubs. tuskar_api.tuskarclient = lambda request: self.stub_tuskarclient() - tuskar_api.baremetalclient = lambda request:\ - self.stub_baremetalclient() def tearDown(self): super(APITestCase, self).tearDown() tuskar_api.tuskarclient = self._original_tuskarclient - tuskar_api.baremetalclient = self._original_baremetalclient def stub_tuskarclient(self): if not hasattr(self, "tuskarclient"): self.mox.StubOutWithMock(tuskar_client, 'Client') self.tuskarclient = self.mox.CreateMock(tuskar_client.Client) return self.tuskarclient - - def stub_baremetalclient(self): - if not hasattr(self, "baremetalclient"): - self.baremetalclient = baremetal.BareMetalNodeManager(None) - return self.baremetalclient diff --git a/tuskar_ui/test/test_data/tuskar_data.py b/tuskar_ui/test/test_data/tuskar_data.py index b1f9f190e..5fa9d9539 100644 --- a/tuskar_ui/test/test_data/tuskar_data.py +++ b/tuskar_ui/test/test_data/tuskar_data.py @@ -10,208 +10,131 @@ # License for the specific language governing permissions and limitations # under the License. -from tuskar_ui import api - from openstack_dashboard.test.test_data import utils as test_data_utils -from novaclient.v1_1.contrib import baremetal -from tuskarclient.v1 import flavors -from tuskarclient.v1 import nodes as tuskar_nodes -from tuskarclient.v1 import racks -from tuskarclient.v1 import resource_classes +from heatclient.v1 import resources +from heatclient.v1 import stacks +from ironicclient.v1 import chassis +from ironicclient.v1 import node +from ironicclient.v1 import port def data(TEST): - # Flavors - TEST.tuskarclient_flavors = test_data_utils.TestDataContainer() - TEST.tuskar_flavors = test_data_utils.TestDataContainer() - flavor_1 = flavors.Flavor(flavors.FlavorManager(None), - {'id': '1', - 'name': 'nano', - 'max_vms': 100, - 'capacities': [ - {"name": "cpu", - "value": 64, - "unit": "CPU"}, - {"name": "memory", - "value": 1024, - "unit": "MB"}, - {"name": "storage", - "value": 1, - "unit": "GB"}, - {"name": "ephemeral_disk", - "value": 0, - "unit": "GB"}, - {"name": "swap_disk", - "value": 2, - "unit": "GB"}]}) - flavor_2 = flavors.Flavor(flavors.FlavorManager(None), - {'id': '2', - 'name': 'large', - 'max_vms': 10, - 'capacities': []}) - TEST.tuskarclient_flavors.add(flavor_1, flavor_2) - TEST.tuskar_flavors.add(api.Flavor(flavor_1), api.Flavor(flavor_2)) - # Resource Classes - TEST.tuskarclient_resource_classes = test_data_utils.TestDataContainer() - TEST.tuskar_resource_classes = test_data_utils.TestDataContainer() - resource_class_1 = resource_classes.ResourceClass( - resource_classes.ResourceClassManager(None), + # Stack + TEST.heatclient_stacks = test_data_utils.TestDataContainer() + stack_1 = stacks.Stack( + stacks.StackManager(None), {'id': '1', - 'service_type': 'compute', - 'racks': [{'id': 1}, {'id': 2}], - 'name': 'rclass1'}) - resource_class_2 = resource_classes.ResourceClass( - resource_classes.ResourceClassManager(None), - {'id': '2', - 'service_type': 'compute', - 'racks': [], - 'name': 'rclass2'}) - TEST.tuskarclient_resource_classes.add(resource_class_1, resource_class_2) - TEST.tuskar_resource_classes.add(api.ResourceClass(resource_class_1), - api.ResourceClass(resource_class_2)) + 'stack_name': 'overcloud', + 'stack_status': 'RUNNING'}) + TEST.heatclient_stacks.add(stack_1) - #Racks - TEST.tuskarclient_racks = test_data_utils.TestDataContainer() - TEST.tuskar_racks = test_data_utils.TestDataContainer() - rack_1 = racks.Rack(racks.RackManager(None), - {'id': '1', - 'name': 'rack1', - 'location': 'location', - 'subnet': '192.168.1.0/24', - 'state': 'active', - 'nodes': [ - {'id': '1'}, - {'id': '2'}, - {'id': '3'}, - {'id': '4'}], - 'capacities': [ - {"name": "total_cpu", - "value": "64", - "unit": "CPU"}, - {"name": "total_memory", - "value": "1024", - "unit": "MB"}], - 'resource_class': {'id': '1'}}) - rack_2 = racks.Rack(racks.RackManager(None), - {'id': '2', - 'name': 'rack2', - 'location': 'location', - 'subnet': '192.168.1.0/25', - 'state': 'provisioning', - 'nodes': [], - 'capacities': [ - {"name": "total_cpu", - "value": "1", - "unit": "CPU"}, - {"name": "total_memory", - "value": "4", - "unit": "MB"}], - 'resource_class': {'id': '1'}}) - rack_3 = racks.Rack(racks.RackManager(None), - {'id': '3', - 'name': 'rack3', - 'location': 'location', - 'subnet': '192.168.1.0/26', - 'state': 'inactive', - 'nodes': [], - 'capacities': [ - {"name": "total_cpu", - "value": "1", - "unit": "CPU"}, - {"name": "total_memory", - "value": "2", - "unit": "MB"}], - }) - TEST.tuskarclient_racks.add(rack_1, rack_2, rack_3) - TEST.tuskar_racks.add(api.Rack(rack_1), api.Rack(rack_2), api.Rack(rack_3)) + # Node + TEST.ironicclient_nodes = test_data_utils.TestDataContainer() + node_1 = node.Node( + node.NodeManager(None), + {'uuid': 'aa-11', + 'chassis_id': 'chassis-id-1', + 'instance_uuid': 'aa', + 'driver': 'pxe_ipmitool', + 'driver_info': { + 'ipmi_address': '1.1.1.1', + 'ipmi_username': 'admin', + 'ipmi_password': 'password', + }, + 'properties': { + 'cpu': '8', + 'ram': '16', + 'local_disk': '10', + }, + 'power_state': 'on'}) + node_2 = node.Node( + node.NodeManager(None), + {'uuid': 'bb-22', + 'chassis_id': 'chassis-id-1', + 'instance_uuid': 'bb', + 'driver': 'pxe_ipmitool', + 'driver_info': { + 'ipmi_address': '2.2.2.2', + 'ipmi_username': 'admin', + 'ipmi_password': 'password', + }, + 'properties': { + 'cpu': '16', + 'ram': '32', + 'local_disk': '100', + }, + 'power_state': 'on'}) + node_3 = node.Node( + node.NodeManager(None), + {'uuid': 'cc-33', + 'chassis_id': 'chassis-id-1', + 'instance_uuid': None, + 'driver': 'pxe_ipmitool', + 'driver_info': { + 'ipmi_address': '3.3.3.3', + 'ipmi_username': 'admin', + 'ipmi_password': 'password', + }, + 'properties': { + 'cpu': '32', + 'ram': '64', + 'local_disk': '1', + }, + 'power_state': 'rebooting'}) + TEST.ironicclient_nodes.add(node_1, node_2, node_3) - # Nodes - TEST.tuskarclient_nodes = test_data_utils.TestDataContainer() - TEST.tuskar_nodes = test_data_utils.TestDataContainer() + # Chassis + TEST.ironicclient_chassis = test_data_utils.TestDataContainer() + chassis_1 = chassis.Chassis( + chassis.ChassisManager(None), + {'uuid': 'chassis-id-1', + 'description': 'default chassis'}) + TEST.ironicclient_chassis.add(chassis_1) - tuskar_node_1 = tuskar_nodes.Node( - tuskar_nodes.NodeManager(None), - {'id': '1', - 'nova_baremetal_node_id': '11', - 'rack': {"id": "1"}}) - tuskar_node_2 = tuskar_nodes.Node( - tuskar_nodes.NodeManager(None), - {'id': '2', - 'nova_baremetal_node_id': '12', - 'rack': {"id": "1"}}) - tuskar_node_3 = tuskar_nodes.Node( - tuskar_nodes.NodeManager(None), - {'id': '3', - 'nova_baremetal_node_id': '13', - 'rack': {"id": "1"}}) - tuskar_node_4 = tuskar_nodes.Node( - tuskar_nodes.NodeManager(None), - {'id': '4', - 'nova_baremetal_node_id': '14', - 'rack': {"id": "1"}}) + # Ports + TEST.ironicclient_ports = test_data_utils.TestDataContainer() + port_1 = port.Port( + port.PortManager(None), + {'id': '1-port-id', + 'type': 'port', + 'address': 'aa:aa:aa:aa:aa:aa'}) + port_2 = port.Port( + port.PortManager(None), + {'id': '2-port-id', + 'type': 'port', + 'address': 'bb:bb:bb:bb:bb:bb'}) + port_3 = port.Port( + port.PortManager(None), + {'id': '3-port-id', + 'type': 'port', + 'address': 'cc:cc:cc:cc:cc:cc'}) + port_4 = port.Port( + port.PortManager(None), + {'id': '4-port-id', + 'type': 'port', + 'address': 'dd:dd:dd:dd:dd:dd'}) + TEST.ironicclient_ports.add(port_1, port_2, port_3, port_4) - TEST.tuskarclient_nodes.add(tuskar_node_1, - tuskar_node_2, - tuskar_node_3, - tuskar_node_4) - TEST.tuskar_nodes.add(api.TuskarNode(tuskar_node_1), - api.TuskarNode(tuskar_node_2), - api.TuskarNode(tuskar_node_3), - api.TuskarNode(tuskar_node_4)) + # Resource + TEST.heatclient_resources = test_data_utils.TestDataContainer() + resource_1 = resources.Resource( + resources.ResourceManager(None), + {'stack_id': '1', + 'resource_name': 'Compute', + 'logical_resource_id': 'Compute', + 'physical_resource_id': 'aa', + 'resource_status': 'CREATE_COMPLETE', + 'resource_type': 'AWS::EC2::Instance'}) + resource_2 = resources.Resource( + resources.ResourceManager(None), + {'stack_id': '1', + 'resource_name': 'Control', + 'logical_resource_id': 'Control', + 'physical_resource_id': 'bb', + 'resource_status': 'CREATE_COMPLETE', + 'resource_type': 'AWS::EC2::Instance'}) + TEST.heatclient_resources.add(resource_1, resource_2) - TEST.baremetalclient_nodes = test_data_utils.TestDataContainer() - TEST.baremetal_nodes = test_data_utils.TestDataContainer() - TEST.baremetalclient_unracked_nodes = test_data_utils.TestDataContainer() - TEST.baremetal_unracked_nodes = test_data_utils.TestDataContainer() - TEST.baremetalclient_nodes_all = test_data_utils.TestDataContainer() - TEST.baremetal_nodes_all = test_data_utils.TestDataContainer() - - baremetal_node_1 = baremetal.BareMetalNode( - baremetal.BareMetalNodeManager(None), - {'instance_uuid': 'uuid_11', - 'id': '11', - 'name': 'node1', - 'prov_mac_address': '00:B0:D0:86:AB:F7'}) - baremetal_node_2 = baremetal.BareMetalNode( - baremetal.BareMetalNodeManager(None), - {'instance_uuid': 'uuid_12', - 'id': '12', - 'name': 'node2', - 'prov_mac_address': '00:B0:D0:86:AB:F8'}) - baremetal_node_3 = baremetal.BareMetalNode( - baremetal.BareMetalNodeManager(None), - {'instance_uuid': 'uuid_13', - 'id': '13', - 'name': 'node3', - 'prov_mac_address': '00:B0:D0:86:AB:F9'}) - baremetal_node_4 = baremetal.BareMetalNode( - baremetal.BareMetalNodeManager(None), - {'instance_uuid': 'uuid_14', - 'id': '14', - 'name': 'node4', - 'prov_mac_address': '00:B0:D0:86:AB:F0'}) - baremetal_node_5 = baremetal.BareMetalNode( - baremetal.BareMetalNodeManager(None), - {'instance_uuid': 'uuid_15', - 'id': '15', - 'name': 'node5', - 'prov_mac_address': '00:B0:D0:86:AB:F1'}) - - TEST.baremetalclient_nodes.add(baremetal_node_1, baremetal_node_2, - baremetal_node_3, baremetal_node_4) - TEST.baremetal_nodes.add(api.BaremetalNode(baremetal_node_1), - api.BaremetalNode(baremetal_node_2), - api.BaremetalNode(baremetal_node_3), - api.BaremetalNode(baremetal_node_4)) - TEST.baremetalclient_unracked_nodes.add(baremetal_node_5) - TEST.baremetal_unracked_nodes.add(api.TuskarNode(baremetal_node_5)) - TEST.baremetalclient_nodes_all.add(baremetal_node_1, baremetal_node_2, - baremetal_node_3, baremetal_node_4, - baremetal_node_5) - TEST.baremetal_nodes_all.add(api.BaremetalNode(baremetal_node_1), - api.BaremetalNode(baremetal_node_2), - api.BaremetalNode(baremetal_node_3), - api.BaremetalNode(baremetal_node_4), - api.BaremetalNode(baremetal_node_5)) + # ResourceCategory