From 3d814c9b970cd204fdafab61341927ad2d700cc8 Mon Sep 17 00:00:00 2001 From: Ana Krivokapic Date: Wed, 6 Aug 2014 19:14:41 +0200 Subject: [PATCH] Replace mocked Ironic data with real API calls Change-Id: Ia84d4ba473623a563df22d833941e46f82432ec6 --- tuskar_ui/api/node.py | 101 +++++++++++------- tuskar_ui/infrastructure/nodes/forms.py | 8 +- tuskar_ui/infrastructure/nodes/tables.py | 6 +- .../templates/nodes/_nodes_formset_form.html | 2 +- tuskar_ui/infrastructure/nodes/tests.py | 16 +-- tuskar_ui/test/test_data/node_data.py | 18 ++-- 6 files changed, 89 insertions(+), 62 deletions(-) diff --git a/tuskar_ui/api/node.py b/tuskar_ui/api/node.py index cc16abe6a..bf81e8488 100644 --- a/tuskar_ui/api/node.py +++ b/tuskar_ui/api/node.py @@ -14,6 +14,7 @@ import logging from django.utils.translation import ugettext_lazy as _ from horizon.utils import memoized +from ironicclient import client as ironic_client from novaclient.v1_1.contrib import baremetal from openstack_dashboard.api import base from openstack_dashboard.api import glance @@ -39,6 +40,13 @@ def baremetalclient(request): return baremetal.BareMetalNodeManager(nc) +def ironicclient(request): + api_version = 1 + kwargs = {'os_auth_token': request.user.token.id, + 'ironic_url': base.url_for(request, 'baremetal')} + return ironic_client.get_client(api_version, **kwargs) + + # FIXME(lsmola) This should be done in Horizon, they don't have caching @memoized.memoized def image_get(request, image_id): @@ -48,7 +56,7 @@ def image_get(request, image_id): with supplied identifier. :param image_id: list of objects to be put into a dict - :type object_list: list + :type image_id: list :return: object :rtype: glanceclient.v1.images.Image @@ -61,14 +69,37 @@ class IronicNode(base.APIResourceWrapper): _attrs = ('id', 'uuid', 'instance_uuid', 'driver', 'driver_info', 'properties', 'power_state', 'maintenance') + def __init__(self, apiresource, request=None): + super(IronicNode, self).__init__(apiresource) + self._request = request + @classmethod - def create(cls, request, ipmi_address, architecture, cpus, memory_mb, + def create(cls, request, ipmi_address, cpu_arch, cpus, memory_mb, local_gb, mac_addresses, ipmi_username=None, ipmi_password=None, driver=None): """Create a Node in Ironic """ - node = TEST_DATA.ironicclient_nodes.first() - return cls(node) + node = ironicclient(request).node.create( + driver=driver, + driver_info={ + 'ipmi_address': ipmi_address, + 'ipmi_username': ipmi_username, + 'password': ipmi_password + }, + properties={ + 'cpus': cpus, + 'memory_mb': memory_mb, + 'local_gb': local_gb, + 'cpu_arch': cpu_arch, + } + ) + for mac_address in mac_addresses: + ironicclient(request).port.create( + node_uuid=node.uuid, + address=mac_address + ) + + return cls(node, request) @classmethod def get(cls, request, uuid): @@ -83,10 +114,8 @@ class IronicNode(base.APIResourceWrapper): :return: matching IronicNode, or None if no IronicNode matches the ID :rtype: tuskar_ui.api.node.IronicNode """ - nodes = IronicNode.list(request) + IronicNode.list_discovered(request) - for node in nodes: - if node.uuid == uuid: - return node + node = ironicclient(request).node.get(uuid) + return cls(node, request) @classmethod def get_by_instance_uuid(cls, request, instance_uuid): @@ -105,9 +134,8 @@ class IronicNode(base.APIResourceWrapper): :raises: ironicclient.exc.HTTPNotFound if there is no IronicNode with the matching instance UUID """ - for node in IronicNode.list(request): - if node.instance_uuid == instance_uuid: - return node + node = ironicclient(request).node.get_by_instance_uuid(instance_uuid) + return cls(node, request) @classmethod @handle_errors(_("Unable to retrieve nodes"), []) @@ -125,26 +153,17 @@ class IronicNode(base.APIResourceWrapper): :return: list of IronicNodes, or an empty list if there are none :rtype: list of tuskar_ui.api.node.IronicNode """ - nodes = [node for node in TEST_DATA.ironicclient_nodes.list() - if not node.newly_discovered] - if associated is not None: - if associated: - nodes = [node for node in nodes - if node.instance_uuid is not None] - else: - nodes = [node for node in nodes - if node.instance_uuid is None] - - return [cls(node) for node in nodes] + nodes = ironicclient(request).node.list(associated=associated) + return [cls(cls.get(request, node.uuid), request) for node in nodes] @classmethod @handle_errors(_("Unable to retrieve newly discovered nodes"), []) def list_discovered(cls, request): """Return a list of IronicNodes which have been newly discovered """ - nodes = [node for node in TEST_DATA.ironicclient_nodes.list() - if node.newly_discovered] - return [cls(node) for node in nodes] + nodes = [node for node in ironicclient(request).node.list() + if getattr(node, 'newly_discovered', False)] + return [cls(cls.get(request, node.uuid), request) for node in nodes] @classmethod def delete(cls, request, uuid): @@ -157,7 +176,19 @@ class IronicNode(base.APIResourceWrapper): :param uuid: ID of IronicNode to be removed :type uuid: str """ - return + return ironicclient(request).node.delete(uuid) + + @classmethod + def list_ports(cls, request, uuid): + """Return a list of ports associated with this IronicNode + + :param request: request object + :type request: django.http.HttpRequest + + :param uuid: ID of IronicNode + :type uuid: str + """ + return ironicclient(request).node.list_ports(uuid) @cached_property def addresses(self): @@ -168,11 +199,7 @@ class IronicNode(base.APIResourceWrapper): this IronicNode :rtype: list of str """ - # we don't use an association in the node test data, because that - # association is unclear (to me); the REST API uses item links that - # are difficult to simulate. for mock purposes, no harm in just - # returning all ports - ports = TEST_DATA.ironicclient_ports.list() + ports = IronicNode.list_ports(self._request, self.uuid) return [port.address for port in ports] @cached_property @@ -188,8 +215,8 @@ class IronicNode(base.APIResourceWrapper): return self.properties['local_gb'] @cached_property - def arch(self): - return self.properties['arch'] + def cpu_arch(self): + return self.properties['cpu_arch'] class BareMetalNode(base.APIResourceWrapper): @@ -197,7 +224,7 @@ class BareMetalNode(base.APIResourceWrapper): 'task_state', 'pm_user', 'pm_address', 'interfaces') @classmethod - def create(cls, request, ipmi_address, architecture, cpus, memory_mb, + def create(cls, request, ipmi_address, cpu_arch, cpus, memory_mb, local_gb, mac_addresses, ipmi_username=None, ipmi_password=None, driver=None): """Create a Nova BareMetalNode @@ -367,7 +394,7 @@ class NodeClient(object): class Node(base.APIResourceWrapper): _attrs = ('id', 'uuid', 'instance_uuid', 'driver', 'driver_info', 'power_state', 'addresses', 'maintenance', 'cpus', - 'memory_mb', 'local_gb', 'arch') + 'memory_mb', 'local_gb', 'cpu_arch') def __init__(self, apiresource, request=None, **kwargs): """Initialize a Node @@ -390,11 +417,11 @@ class Node(base.APIResourceWrapper): self._instance = kwargs['instance'] @classmethod - def create(cls, request, ipmi_address, architecture, cpus, memory_mb, + def create(cls, request, ipmi_address, cpu_arch, cpus, memory_mb, local_gb, mac_addresses, ipmi_username=None, ipmi_password=None, driver=None): return cls(NodeClient(request).node_class.create( - request, ipmi_address, architecture, cpus, memory_mb, local_gb, + request, ipmi_address, cpu_arch, cpus, memory_mb, local_gb, mac_addresses, ipmi_username=ipmi_username, ipmi_password=ipmi_password, driver=driver)) diff --git a/tuskar_ui/infrastructure/nodes/forms.py b/tuskar_ui/infrastructure/nodes/forms.py index d61df8f7d..a18a086b4 100644 --- a/tuskar_ui/infrastructure/nodes/forms.py +++ b/tuskar_ui/infrastructure/nodes/forms.py @@ -20,7 +20,7 @@ from tuskar_ui import api import tuskar_ui.forms -ARCHITECTURE_CHOICES = [ +CPU_ARCH_CHOICES = [ ('x86', _("x86")), ('x86_64', _("x86_64")), ] @@ -82,10 +82,10 @@ class NodeForm(django.forms.Form): }), ) - architecture = django.forms.ChoiceField( + cpu_arch = django.forms.ChoiceField( label=_("Architecture"), required=True, - choices=ARCHITECTURE_CHOICES, + choices=CPU_ARCH_CHOICES, widget=django.forms.Select( attrs={'class': 'form-control'}), ) @@ -135,7 +135,7 @@ class BaseNodeFormset(django.forms.formsets.BaseFormSet): # TODO(rdopieralski) If ipmi_address is no longer required, # then we will need to use something else here? ipmi_address=data['ipmi_address'], - architecture=data.get('architecture'), + cpu_arch=data.get('cpu_arch'), cpus=data.get('cpus'), memory_mb=data.get('memory_mb'), local_gb=data.get('local_gb'), diff --git a/tuskar_ui/infrastructure/nodes/tables.py b/tuskar_ui/infrastructure/nodes/tables.py index 446d3ea7a..2e11c1769 100644 --- a/tuskar_ui/infrastructure/nodes/tables.py +++ b/tuskar_ui/infrastructure/nodes/tables.py @@ -29,7 +29,7 @@ class DeleteNode(tables.BatchAction): classes = ('btn-danger',) def allowed(self, request, obj=None): - return getattr(obj, 'instance', None) is None + return getattr(obj, 'instance_uuid', None) is None def action(self, request, obj_id): api.node.Node.delete(request, obj_id) @@ -114,8 +114,8 @@ class IronicDiscoveredNodesTable(tables.DataTable): node = tables.Column('uuid', link="horizon:infrastructure:nodes:detail", verbose_name=_("Node Name")) - arch = tables.Column('arch', - verbose_name=_("Arch.")) + cpu_arch = tables.Column('cpu_arch', + verbose_name=_("Arch.")) cpus = tables.Column('cpus', verbose_name=_("CPU (cores)")) memory_mb = tables.Column('memory_mb', diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/_nodes_formset_form.html b/tuskar_ui/infrastructure/nodes/templates/nodes/_nodes_formset_form.html index 289f4e671..28cb45d13 100644 --- a/tuskar_ui/infrastructure/nodes/templates/nodes/_nodes_formset_form.html +++ b/tuskar_ui/infrastructure/nodes/templates/nodes/_nodes_formset_form.html @@ -25,7 +25,7 @@
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.architecture required=True %} + {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.cpu_arch required=True %} {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.cpus extra_text=_('units') required=True %} {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.memory_mb extra_text=_('MB') required=True %} {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.local_gb extra_text=_('GB') required=True %} diff --git a/tuskar_ui/infrastructure/nodes/tests.py b/tuskar_ui/infrastructure/nodes/tests.py index 312334855..fc2367717 100644 --- a/tuskar_ui/infrastructure/nodes/tests.py +++ b/tuskar_ui/infrastructure/nodes/tests.py @@ -123,7 +123,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): 'register_nodes-0-ipmi_username': 'username', 'register_nodes-0-ipmi_password': 'password', 'register_nodes-0-mac_addresses': 'de:ad:be:ef:ca:fe', - 'register_nodes-0-architecture': 'x86', + 'register_nodes-0-cpu_arch': 'x86', 'register_nodes-0-cpus': '1', 'register_nodes-0-memory_mb': '2', 'register_nodes-0-local_gb': '3', @@ -131,7 +131,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): 'register_nodes-1-driver': 'ipmi', 'register_nodes-1-ipmi_address': '127.0.0.2', 'register_nodes-1-mac_addresses': 'de:ad:be:ef:ca:ff', - 'register_nodes-1-architecture': 'x86', + 'register_nodes-1-cpu_arch': 'x86', 'register_nodes-1-cpus': '4', 'register_nodes-1-memory_mb': '5', 'register_nodes-1-local_gb': '6', @@ -147,7 +147,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): call( request, ipmi_address=u'127.0.0.1', - architecture='x86', + cpu_arch='x86', cpus=1, memory_mb=2, local_gb=3, @@ -159,7 +159,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): call( request, ipmi_address=u'127.0.0.2', - architecture='x86', + cpu_arch='x86', cpus=4, memory_mb=5, local_gb=6, @@ -181,7 +181,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): 'register_nodes-0-ipmi_username': 'username', 'register_nodes-0-ipmi_password': 'password', 'register_nodes-0-mac_addresses': 'de:ad:be:ef:ca:fe', - 'register_nodes-0-architecture': 'x86', + 'register_nodes-0-cpu_arch': 'x86', 'register_nodes-0-cpus': '1', 'register_nodes-0-memory_mb': '2', 'register_nodes-0-local_gb': '3', @@ -189,7 +189,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): 'register_nodes-1-driver': 'ipmi', 'register_nodes-1-ipmi_address': '127.0.0.2', 'register_nodes-1-mac_addresses': 'de:ad:be:ef:ca:ff', - 'register_nodes-1-architecture': 'x86', + 'register_nodes-1-cpu_arch': 'x86', 'register_nodes-1-cpus': '4', 'register_nodes-1-memory_mb': '5', 'register_nodes-1-local_gb': '6', @@ -205,7 +205,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): call( request, ipmi_address=u'127.0.0.1', - architecture='x86', + cpu_arch='x86', cpus=1, memory_mb=2, local_gb=3, @@ -217,7 +217,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): call( request, ipmi_address=u'127.0.0.2', - architecture='x86', + cpu_arch='x86', cpus=4, memory_mb=5, local_gb=6, diff --git a/tuskar_ui/test/test_data/node_data.py b/tuskar_ui/test/test_data/node_data.py index 1dc81b199..52127c76b 100644 --- a/tuskar_ui/test/test_data/node_data.py +++ b/tuskar_ui/test/test_data/node_data.py @@ -131,7 +131,7 @@ def data(TEST): 'cpus': '8', 'memory_mb': '4096', 'local_gb': '10', - 'arch': 'x86_64', + 'cpu_arch': 'x86_64', }, 'power_state': 'on', 'maintenance': None, @@ -153,7 +153,7 @@ def data(TEST): 'cpus': '16', 'memory_mb': '4096', 'local_gb': '100', - 'arch': 'x86_64', + 'cpu_arch': 'x86_64', }, 'power_state': 'on', 'maintenance': None, @@ -175,7 +175,7 @@ def data(TEST): 'cpus': '32', 'memory_mb': '8192', 'local_gb': '1', - 'arch': 'x86_64', + 'cpu_arch': 'x86_64', }, 'power_state': 'rebooting', 'maintenance': None, @@ -197,7 +197,7 @@ def data(TEST): 'cpus': '8', 'memory_mb': '4096', 'local_gb': '10', - 'arch': 'x86_64', + 'cpu_arch': 'x86_64', }, 'power_state': 'on', 'maintenance': None, @@ -219,7 +219,7 @@ def data(TEST): 'cpus': '8', 'memory_mb': '4096', 'local_gb': '10', - 'arch': 'x86_64', + 'cpu_arch': 'x86_64', }, 'power_state': 'error', 'maintenance': None, @@ -241,7 +241,7 @@ def data(TEST): 'cpus': '8', 'memory_mb': '4096', 'local_gb': '10', - 'arch': 'x86_64', + 'cpu_arch': 'x86_64', }, 'power_state': 'on', 'maintenance': None, @@ -263,7 +263,7 @@ def data(TEST): 'cpus': '8', 'memory_mb': '4096', 'local_gb': '10', - 'arch': 'x86_64', + 'cpu_arch': 'x86_64', }, 'power_state': 'on', 'maintenance': True, @@ -285,7 +285,7 @@ def data(TEST): 'cpus': '8', 'memory_mb': '4096', 'local_gb': '10', - 'arch': 'x86_64', + 'cpu_arch': 'x86_64', }, 'power_state': 'on', 'maintenance': True, @@ -307,7 +307,7 @@ def data(TEST): 'cpus': '16', 'memory_mb': '8192', 'local_gb': '1000', - 'arch': 'x86_64', + 'cpu_arch': 'x86_64', }, 'power_state': 'on', 'maintenance': True,