summaryrefslogtreecommitdiff
path: root/tuskar_ui/api/node.py
diff options
context:
space:
mode:
Diffstat (limited to 'tuskar_ui/api/node.py')
-rw-r--r--tuskar_ui/api/node.py445
1 files changed, 0 insertions, 445 deletions
diff --git a/tuskar_ui/api/node.py b/tuskar_ui/api/node.py
deleted file mode 100644
index e9fef71b..00000000
--- a/tuskar_ui/api/node.py
+++ /dev/null
@@ -1,445 +0,0 @@
-# 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.
-
-import logging
-import time
-
-from django.conf import settings
-from django.utils.translation import ugettext_lazy as _
-from horizon.utils import memoized
-from ironic_inspector_client import client as inspector_client
-from ironicclient import client as ironic_client
-from openstack_dashboard.api import base
-from openstack_dashboard.api import glance
-from openstack_dashboard.api import nova
-
-from tuskar_ui.cached_property import cached_property # noqa
-from tuskar_ui.handle_errors import handle_errors # noqa
-from tuskar_ui.utils import utils
-
-
-# power states
-ERROR_STATES = set(['deploy failed', 'error'])
-POWER_ON_STATES = set(['on', 'power on'])
-
-# provision_states of ironic aggregated to reasonable groups
-PROVISION_STATE_FREE = ['available', 'deleted', None]
-PROVISION_STATE_PROVISIONED = ['active']
-PROVISION_STATE_PROVISIONING = [
- 'deploying', 'wait call-back', 'rebuild', 'deploy complete']
-PROVISION_STATE_DELETING = ['deleting']
-PROVISION_STATE_ERROR = ['error', 'deploy failed']
-
-# names for states of ironic used in UI,
-# provison_states + discovery states
-DISCOVERING_STATE = 'discovering'
-DISCOVERED_STATE = 'discovered'
-DISCOVERY_FAILED_STATE = 'discovery failed'
-MAINTENANCE_STATE = 'manageable'
-PROVISIONED_STATE = 'provisioned'
-PROVISIONING_FAILED_STATE = 'provisioning failed'
-PROVISIONING_STATE = 'provisioning'
-DELETING_STATE = 'deleting'
-FREE_STATE = 'free'
-
-
-IRONIC_DISCOVERD_URL = getattr(settings, 'IRONIC_DISCOVERD_URL', None)
-LOG = logging.getLogger(__name__)
-
-
-@memoized.memoized
-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
-@handle_errors(_("Unable to retrieve image."))
-def image_get(request, image_id):
- """Returns an Image object with metadata
-
- Returns an Image object populated with metadata for image
- with supplied identifier.
-
- :param image_id: list of objects to be put into a dict
- :type image_id: list
-
- :return: object
- :rtype: glanceclient.v1.images.Image
- """
- image = glance.image_get(request, image_id)
- return image
-
-
-class Node(base.APIResourceWrapper):
- _attrs = ('id', 'uuid', 'instance_uuid', 'driver', 'driver_info',
- 'properties', 'power_state', 'target_power_state',
- 'provision_state', 'maintenance', 'extra')
-
- def __init__(self, apiresource, request=None, instance=None):
- """Initialize a Node
-
- :param apiresource: apiresource we want to wrap
- :type apiresource: IronicNode
-
- :param request: request
- :type request: django.core.handlers.wsgi.WSGIRequest
-
- :param instance: instance relation we want to cache
- :type instance: openstack_dashboard.api.nova.Server
-
- :return: Node object
- :rtype: tusar_ui.api.node.Node
- """
- super(Node, self).__init__(apiresource)
- self._request = request
- self._instance = instance
-
- @classmethod
- def create(cls, request, ipmi_address=None, cpu_arch=None, cpus=None,
- memory_mb=None, local_gb=None, mac_addresses=[],
- ipmi_username=None, ipmi_password=None, ssh_address=None,
- ssh_username=None, ssh_key_contents=None,
- deployment_kernel=None, deployment_ramdisk=None,
- driver=None):
- """Create a Node in Ironic."""
- if driver == 'pxe_ssh':
- driver_info = {
- 'ssh_address': ssh_address,
- 'ssh_username': ssh_username,
- 'ssh_key_contents': ssh_key_contents,
- 'ssh_virt_type': 'virsh',
- }
- else:
- driver_info = {
- 'ipmi_address': ipmi_address,
- 'ipmi_username': ipmi_username,
- 'ipmi_password': ipmi_password
- }
- driver_info.update(
- deploy_kernel=deployment_kernel,
- deploy_ramdisk=deployment_ramdisk
- )
-
- properties = {'capabilities': 'boot_option:local', }
- if cpus:
- properties.update(cpus=cpus)
- if memory_mb:
- properties.update(memory_mb=memory_mb)
- if local_gb:
- properties.update(local_gb=local_gb)
- if cpu_arch:
- properties.update(cpu_arch=cpu_arch)
-
- node = ironicclient(request).node.create(
- driver=driver,
- driver_info=driver_info,
- properties=properties,
- )
- for mac_address in mac_addresses:
- ironicclient(request).port.create(
- node_uuid=node.uuid,
- address=mac_address
- )
-
- return cls(node, request)
-
- @classmethod
- @memoized.memoized
- @handle_errors(_("Unable to retrieve node"))
- def get(cls, request, uuid):
- """Return the Node that matches the ID
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuid: ID of Node to be retrieved
- :type uuid: str
-
- :return: matching Node, or None if no IronicNode matches the ID
- :rtype: tuskar_ui.api.node.Node
- """
- node = ironicclient(request).node.get(uuid)
- if node.instance_uuid is not None:
- server = nova.server_get(request, node.instance_uuid)
- else:
- server = None
- return cls(node, request, server)
-
- @classmethod
- @handle_errors(_("Unable to retrieve node"))
- def get_by_instance_uuid(cls, request, instance_uuid):
- """Return the Node associated with the instance ID
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param instance_uuid: ID of Instance that is deployed on the Node
- to be retrieved
- :type instance_uuid: str
-
- :return: matching Node
- :rtype: tuskar_ui.api.node.Node
-
- :raises: ironicclient.exc.HTTPNotFound if there is no Node with
- the matching instance UUID
- """
- node = ironicclient(request).node.get_by_instance_uuid(instance_uuid)
- server = nova.server_get(request, instance_uuid)
- return cls(node, request, server)
-
- @classmethod
- @memoized.memoized
- @handle_errors(_("Unable to retrieve nodes"), [])
- def list(cls, request, associated=None, maintenance=None):
- """Return a list of Nodes
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param associated: should we also retrieve all Nodes, only those
- associated with an Instance, or only those not
- associated with an Instance?
- :type associated: bool
-
- :param maintenance: should we also retrieve all Nodes, only those
- in maintenance mode, or those which are not in
- maintenance mode?
- :type maintenance: bool
-
- :return: list of Nodes, or an empty list if there are none
- :rtype: list of tuskar_ui.api.node.Node
- """
- nodes = ironicclient(request).node.list(associated=associated,
- maintenance=maintenance)
- if associated is None or associated:
- servers = nova.server_list(request)[0]
- servers_dict = utils.list_to_dict(servers)
- nodes_with_instance = []
- for n in nodes:
- server = servers_dict.get(n.instance_uuid, None)
- nodes_with_instance.append(cls(n, instance=server,
- request=request))
- return [cls.get(request, node.uuid)
- for node in nodes_with_instance]
- return [cls.get(request, node.uuid) for node in nodes]
-
- @classmethod
- def delete(cls, request, uuid):
- """Delete an Node
-
- Remove the IronicNode matching the ID if it
- exists; otherwise, does nothing.
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuid: ID of IronicNode to be removed
- :type uuid: str
- """
- return ironicclient(request).node.delete(uuid)
-
- @classmethod
- def discover(cls, request, uuids):
- """Set the maintenance status of node
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuids: IDs of IronicNodes
- :type uuids: list of str
- """
- if not IRONIC_DISCOVERD_URL:
- return
- for uuid in uuids:
-
- inspector_client.introspect(
- uuid,
- base_url=IRONIC_DISCOVERD_URL,
- auth_token=request.user.token.id)
-
- # NOTE(dtantsur): PXE firmware on virtual machines misbehaves when
- # a lot of nodes start DHCPing simultaneously: it ignores NACK from
- # DHCP server, tries to get the same address, then times out. Work
- # around it by using sleep, anyway introspection takes much longer.
- time.sleep(5)
-
- @classmethod
- def set_maintenance(cls, request, uuid, maintenance):
- """Set the maintenance status of node
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuid: ID of Node to be removed
- :type uuid: str
-
- :param maintenance: desired maintenance state
- :type maintenance: bool
- """
- patch = {
- 'op': 'replace',
- 'value': 'True' if maintenance else 'False',
- 'path': '/maintenance'
- }
- node = ironicclient(request).node.update(uuid, [patch])
- return cls(node, request)
-
- @classmethod
- def set_power_state(cls, request, uuid, power_state):
- """Set the power_state of node
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuid: ID of Node
- :type uuid: str
-
- :param power_state: desired power_state
- :type power_state: str
- """
- node = ironicclient(request).node.set_power_state(uuid, power_state)
- return cls(node, request)
-
- @classmethod
- @memoized.memoized
- def list_ports(cls, request, uuid):
- """Return a list of ports associated with this Node
-
- :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):
- """Return a list of port addresses associated with this IronicNode
-
- :return: list of port addresses associated with this IronicNode, or
- an empty list if no addresses are associated with
- this IronicNode
- :rtype: list of str
- """
- ports = self.list_ports(self._request, self.uuid)
- return [port.address for port in ports]
-
- @cached_property
- def cpus(self):
- return self.properties.get('cpus', None)
-
- @cached_property
- def memory_mb(self):
- return self.properties.get('memory_mb', None)
-
- @cached_property
- def local_gb(self):
- return self.properties.get('local_gb', None)
-
- @cached_property
- def cpu_arch(self):
- return self.properties.get('cpu_arch', None)
-
- @cached_property
- def state(self):
- if self.maintenance:
- if not IRONIC_DISCOVERD_URL:
- return MAINTENANCE_STATE
- try:
- status = inspector_client.get_status(
- uuid=self.uuid,
- base_url=IRONIC_DISCOVERD_URL,
- auth_token=self._request.user.token.id,
- )
- except inspector_client.ClientError as e:
- if getattr(e.response, 'status_code', None) == 404:
- return MAINTENANCE_STATE
- raise
- if status['error']:
- return DISCOVERY_FAILED_STATE
- elif status['finished']:
- return DISCOVERED_STATE
- else:
- return DISCOVERING_STATE
- else:
- if self.provision_state in PROVISION_STATE_FREE:
- return FREE_STATE
- if self.provision_state in PROVISION_STATE_PROVISIONING:
- return PROVISIONING_STATE
- if self.provision_state in PROVISION_STATE_PROVISIONED:
- return PROVISIONED_STATE
- if self.provision_state in PROVISION_STATE_DELETING:
- return DELETING_STATE
- if self.provision_state in PROVISION_STATE_ERROR:
- return PROVISIONING_FAILED_STATE
- # Unknown state
- return None
-
- @cached_property
- def instance(self):
- """Return the Nova Instance associated with this Node
-
- :return: Nova Instance associated with this Node; or
- None if there is no Instance associated with this
- Node, or no matching Instance is found
- :rtype: Instance
- """
- if self._instance is not None:
- return self._instance
- if self.instance_uuid:
- servers, _has_more_data = nova.server_list(self._request)
- for server in servers:
- if server.id == self.instance_uuid:
- return server
-
- @cached_property
- def ip_address(self):
- try:
- apiresource = self.instace._apiresource
- except AttributeError:
- LOG.error("Couldn't obtain IP address")
- return None
- return apiresource.addresses['ctlplane'][0]['addr']
-
- @cached_property
- def image_name(self):
- """Return image name of associated instance
-
- Returns image name of instance associated with node
-
- :return: Image name of instance
- :rtype: string
- """
- if self.instance is None:
- return
- image = image_get(self._request, self.instance.image['id'])
- return image.name
-
- @cached_property
- def instance_status(self):
- return getattr(getattr(self, 'instance', None), 'status', None)
-
- @cached_property
- def provisioning_status(self):
- if self.instance_uuid:
- return _("Provisioned")
- return _("Free")
-
- @classmethod
- def get_all_mac_addresses(cls, request):
- macs = [node.addresses for node in cls.list(request)]
- return set([mac.upper() for sublist in macs for mac in sublist])