Merge "Remove python-ironicclient"
This commit is contained in:
commit
108214d9db
@ -1,32 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) 2017 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
for lib in \
|
||||
python-keystoneclient \
|
||||
python-ironicclient \
|
||||
os-client-config \
|
||||
keystoneauth
|
||||
do
|
||||
egg=$(echo $lib | tr '-' '_' | sed 's/python-//')
|
||||
if [ -d /opt/stack/new/$lib ] ; then
|
||||
tip_location="git+file:///opt/stack/new/$lib#egg=$egg"
|
||||
echo "$(which pip) install -U -e $tip_location"
|
||||
pip uninstall -y $lib
|
||||
pip install -U -e $tip_location
|
||||
else
|
||||
echo "$lib not found in /opt/stack/new/$lib"
|
||||
fi
|
||||
done
|
@ -16,9 +16,7 @@ import jsonpatch
|
||||
import munch
|
||||
|
||||
from openstack.cloud.exc import * # noqa
|
||||
from openstack.cloud import meta
|
||||
from openstack.cloud import openstackcloud
|
||||
from openstack.cloud import _tasks
|
||||
from openstack.cloud import _utils
|
||||
|
||||
|
||||
@ -35,29 +33,42 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
ironic_client = None
|
||||
|
||||
def list_nics(self):
|
||||
with _utils.shade_exceptions("Error fetching machine port list"):
|
||||
return meta.obj_list_to_munch(
|
||||
self.manager.submit_task(_tasks.MachinePortList(self)))
|
||||
msg = "Error fetching machine port list"
|
||||
return self._baremetal_client.get("/ports",
|
||||
microversion="1.6",
|
||||
error_message=msg)
|
||||
|
||||
def list_nics_for_machine(self, uuid):
|
||||
with _utils.shade_exceptions(
|
||||
"Error fetching port list for node {node_id}".format(
|
||||
node_id=uuid)):
|
||||
return meta.obj_list_to_munch(self.manager.submit_task(
|
||||
_tasks.MachineNodePortList(self, node_id=uuid)))
|
||||
"""Returns a list of ports present on the machine node.
|
||||
|
||||
:param uuid: String representing machine UUID value in
|
||||
order to identify the machine.
|
||||
:returns: A dictionary containing containing a list of ports,
|
||||
associated with the label "ports".
|
||||
"""
|
||||
msg = "Error fetching port list for node {node_id}".format(
|
||||
node_id=uuid)
|
||||
url = "/nodes/{node_id}/ports".format(node_id=uuid)
|
||||
return self._baremetal_client.get(url,
|
||||
microversion="1.6",
|
||||
error_message=msg)
|
||||
|
||||
def get_nic_by_mac(self, mac):
|
||||
"""Get Machine by MAC address"""
|
||||
# TODO(shade) Finish porting ironic to REST/sdk
|
||||
# try:
|
||||
# return self.manager.submit_task(
|
||||
# _tasks.MachineNodePortGet(self, port_id=mac))
|
||||
# except ironic_exceptions.ClientException:
|
||||
# return None
|
||||
try:
|
||||
url = '/ports/detail?address=%s' % mac
|
||||
data = self._baremetal_client.get(url)
|
||||
if len(data['ports']) == 1:
|
||||
return data['ports'][0]
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def list_machines(self):
|
||||
return self._normalize_machines(
|
||||
self.manager.submit_task(_tasks.MachineNodeList(self)))
|
||||
msg = "Error fetching machine node list"
|
||||
data = self._baremetal_client.get("/nodes",
|
||||
microversion="1.6",
|
||||
error_message=msg)
|
||||
return self._get_and_munchify('nodes', data)
|
||||
|
||||
def get_machine(self, name_or_id):
|
||||
"""Get Machine by name or uuid
|
||||
@ -70,13 +81,20 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
:returns: ``munch.Munch`` representing the node found or None if no
|
||||
nodes are found.
|
||||
"""
|
||||
# TODO(shade) Finish porting ironic to REST/sdk
|
||||
# try:
|
||||
# return self._normalize_machine(
|
||||
# self.manager.submit_task(
|
||||
# _tasks.MachineNodeGet(node_id=name_or_id)))
|
||||
# except ironic_exceptions.ClientException:
|
||||
# return None
|
||||
# NOTE(TheJulia): This is the initial microversion shade support for
|
||||
# ironic was created around. Ironic's default behavior for newer
|
||||
# versions is to expose the field, but with a value of None for
|
||||
# calls by a supported, yet older microversion.
|
||||
# Consensus for moving forward with microversion handling in shade
|
||||
# seems to be to take the same approach, although ironic's API
|
||||
# does it for the user.
|
||||
version = "1.6"
|
||||
try:
|
||||
url = '/nodes/{node_id}'.format(node_id=name_or_id)
|
||||
return self._normalize_machine(
|
||||
self._baremetal_client.get(url, microversion=version))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_machine_by_mac(self, mac):
|
||||
"""Get machine by port MAC address
|
||||
@ -86,13 +104,14 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
:returns: ``munch.Munch`` representing the node found or None
|
||||
if the node is not found.
|
||||
"""
|
||||
# try:
|
||||
# port = self.manager.submit_task(
|
||||
# _tasks.MachinePortGetByAddress(address=mac))
|
||||
# return self.manager.submit_task(
|
||||
# _tasks.MachineNodeGet(node_id=port.node_uuid))
|
||||
# except ironic_exceptions.ClientException:
|
||||
# return None
|
||||
try:
|
||||
port_url = '/ports/detail?address={mac}'.format(mac=mac)
|
||||
port = self._baremetal_client.get(port_url, microversion=1.6)
|
||||
machine_url = '/nodes/{machine}'.format(
|
||||
machine=port['ports'][0]['node_uuid'])
|
||||
return self._baremetal_client.get(machine_url, microversion=1.6)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def inspect_machine(self, name_or_id, wait=False, timeout=3600):
|
||||
"""Inspect a Barmetal machine
|
||||
@ -204,15 +223,27 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
:returns: Returns a ``munch.Munch`` representing the new
|
||||
baremetal node.
|
||||
"""
|
||||
with _utils.shade_exceptions("Error registering machine with Ironic"):
|
||||
machine = self.manager.submit_task(_tasks.MachineCreate(**kwargs))
|
||||
|
||||
msg = ("Baremetal machine node failed to be created.")
|
||||
port_msg = ("Baremetal machine port failed to be created.")
|
||||
|
||||
url = '/nodes'
|
||||
# TODO(TheJulia): At some point we need to figure out how to
|
||||
# handle data across when the requestor is defining newer items
|
||||
# with the older api.
|
||||
machine = self._baremetal_client.post(url,
|
||||
json=kwargs,
|
||||
error_message=msg,
|
||||
microversion="1.6")
|
||||
|
||||
created_nics = []
|
||||
try:
|
||||
for row in nics:
|
||||
nic = self.manager.submit_task(
|
||||
_tasks.MachinePortCreate(address=row['mac'],
|
||||
node_uuid=machine['uuid']))
|
||||
payload = {'address': row['mac'],
|
||||
'node_uuid': machine['uuid']}
|
||||
nic = self._baremetal_client.post('/ports',
|
||||
json=payload,
|
||||
error_message=port_msg)
|
||||
created_nics.append(nic['uuid'])
|
||||
|
||||
except Exception as e:
|
||||
@ -221,14 +252,23 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
try:
|
||||
for uuid in created_nics:
|
||||
try:
|
||||
self.manager.submit_task(
|
||||
_tasks.MachinePortDelete(
|
||||
port_id=uuid))
|
||||
port_url = '/ports/{uuid}'.format(uuid=uuid)
|
||||
# NOTE(TheJulia): Added in hope that it is logged.
|
||||
port_msg = ('Failed to delete port {port} for node'
|
||||
'{node}').format(port=uuid,
|
||||
node=machine['uuid'])
|
||||
self._baremetal_client.delete(port_url,
|
||||
error_message=port_msg)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
self.manager.submit_task(
|
||||
_tasks.MachineDelete(node_id=machine['uuid']))
|
||||
version = "1.6"
|
||||
msg = "Baremetal machine failed to be deleted."
|
||||
url = '/nodes/{node_id}'.format(
|
||||
node_id=machine['uuid'])
|
||||
self._baremetal_client.delete(url,
|
||||
error_message=msg,
|
||||
microversion=version)
|
||||
raise OpenStackCloudException(
|
||||
"Error registering NICs with the baremetal service: %s"
|
||||
% str(e))
|
||||
@ -329,19 +369,44 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
"Error unregistering node '%s' due to current provision "
|
||||
"state '%s'" % (uuid, machine['provision_state']))
|
||||
|
||||
# NOTE(TheJulia) There is a high possibility of a lock being present
|
||||
# if the machine was just moved through the state machine. This was
|
||||
# previously concealed by exception retry logic that detected the
|
||||
# failure, and resubitted the request in python-ironicclient.
|
||||
try:
|
||||
self.wait_for_baremetal_node_lock(machine, timeout=timeout)
|
||||
except OpenStackCloudException as e:
|
||||
raise OpenStackCloudException("Error unregistering node '%s': "
|
||||
"Exception occured while waiting "
|
||||
"to be able to proceed: %s"
|
||||
% (machine['uuid'], e))
|
||||
|
||||
for nic in nics:
|
||||
with _utils.shade_exceptions(
|
||||
"Error removing NIC {nic} from baremetal API for node "
|
||||
"{uuid}".format(nic=nic, uuid=uuid)):
|
||||
port = self.manager.submit_task(
|
||||
_tasks.MachinePortGetByAddress(address=nic['mac']))
|
||||
self.manager.submit_task(
|
||||
_tasks.MachinePortDelete(port_id=port.uuid))
|
||||
port_msg = ("Error removing NIC {nic} from baremetal API for "
|
||||
"node {uuid}").format(nic=nic, uuid=uuid)
|
||||
port_url = '/ports/detail?address={mac}'.format(mac=nic['mac'])
|
||||
port = self._baremetal_client.get(port_url, microversion=1.6,
|
||||
error_message=port_msg)
|
||||
port_url = '/ports/{uuid}'.format(uuid=port['ports'][0]['uuid'])
|
||||
self._baremetal_client.delete(port_url, error_message=port_msg)
|
||||
|
||||
with _utils.shade_exceptions(
|
||||
"Error unregistering machine {node_id} from the baremetal "
|
||||
"API".format(node_id=uuid)):
|
||||
self.manager.submit_task(
|
||||
_tasks.MachineDelete(node_id=uuid))
|
||||
|
||||
# NOTE(TheJulia): While this should not matter microversion wise,
|
||||
# ironic assumes all calls without an explicit microversion to be
|
||||
# version 1.0. Ironic expects to deprecate support for older
|
||||
# microversions in future releases, as such, we explicitly set
|
||||
# the version to what we have been using with the client library..
|
||||
version = "1.6"
|
||||
msg = "Baremetal machine failed to be deleted."
|
||||
url = '/nodes/{node_id}'.format(
|
||||
node_id=uuid)
|
||||
self._baremetal_client.delete(url,
|
||||
error_message=msg,
|
||||
microversion=version)
|
||||
|
||||
if wait:
|
||||
for count in _utils._iterate_timeout(
|
||||
timeout,
|
||||
@ -353,9 +418,7 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
"""Patch Machine Information
|
||||
|
||||
This method allows for an interface to manipulate node entries
|
||||
within Ironic. Specifically, it is a pass-through for the
|
||||
ironicclient.nodes.update interface which allows the Ironic Node
|
||||
properties to be updated.
|
||||
within Ironic.
|
||||
|
||||
:param node_id: The server object to attach to.
|
||||
:param patch:
|
||||
@ -386,15 +449,13 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
:returns: ``munch.Munch`` representing the newly updated node.
|
||||
"""
|
||||
|
||||
with _utils.shade_exceptions(
|
||||
"Error updating machine via patch operation on node "
|
||||
"{node}".format(node=name_or_id)
|
||||
):
|
||||
return self._normalize_machine(
|
||||
self.manager.submit_task(
|
||||
_tasks.MachinePatch(node_id=name_or_id,
|
||||
patch=patch,
|
||||
http_method='PATCH')))
|
||||
msg = ("Error updating machine via patch operation on node "
|
||||
"{node}".format(node=name_or_id))
|
||||
url = '/nodes/{node_id}'.format(node_id=name_or_id)
|
||||
return self._normalize_machine(
|
||||
self._baremetal_client.patch(url,
|
||||
json=patch,
|
||||
error_message=msg))
|
||||
|
||||
def update_machine(self, name_or_id, chassis_uuid=None, driver=None,
|
||||
driver_info=None, name=None, instance_info=None,
|
||||
@ -506,14 +567,20 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
)
|
||||
|
||||
def validate_node(self, uuid):
|
||||
with _utils.shade_exceptions():
|
||||
ifaces = self.manager.submit_task(
|
||||
_tasks.MachineNodeValidate(node_uuid=uuid))
|
||||
# TODO(TheJulia): There are soooooo many other interfaces
|
||||
# that we can support validating, while these are essential,
|
||||
# we should support more.
|
||||
# TODO(TheJulia): Add a doc string :(
|
||||
msg = ("Failed to query the API for validation status of "
|
||||
"node {node_id}").format(node_id=uuid)
|
||||
url = '/nodes/{node_id}/validate'.format(node_id=uuid)
|
||||
ifaces = self._baremetal_client.get(url, error_message=msg)
|
||||
|
||||
if not ifaces.deploy or not ifaces.power:
|
||||
if not ifaces['deploy'] or not ifaces['power']:
|
||||
raise OpenStackCloudException(
|
||||
"ironic node %s failed to validate. "
|
||||
"(deploy: %s, power: %s)" % (ifaces.deploy, ifaces.power))
|
||||
"(deploy: %s, power: %s)" % (ifaces['deploy'],
|
||||
ifaces['power']))
|
||||
|
||||
def node_set_provision_state(self,
|
||||
name_or_id,
|
||||
@ -549,36 +616,44 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
:returns: ``munch.Munch`` representing the current state of the machine
|
||||
upon exit of the method.
|
||||
"""
|
||||
with _utils.shade_exceptions(
|
||||
"Baremetal machine node failed change provision state to "
|
||||
"{state}".format(state=state)
|
||||
):
|
||||
machine = self.manager.submit_task(
|
||||
_tasks.MachineSetProvision(node_uuid=name_or_id,
|
||||
state=state,
|
||||
configdrive=configdrive))
|
||||
# NOTE(TheJulia): Default microversion for this call is 1.6.
|
||||
# Setting locally until we have determined our master plan regarding
|
||||
# microversion handling.
|
||||
version = "1.6"
|
||||
msg = ("Baremetal machine node failed change provision state to "
|
||||
"{state}".format(state=state))
|
||||
|
||||
if wait:
|
||||
for count in _utils._iterate_timeout(
|
||||
timeout,
|
||||
"Timeout waiting for node transition to "
|
||||
"target state of '%s'" % state):
|
||||
machine = self.get_machine(name_or_id)
|
||||
if 'failed' in machine['provision_state']:
|
||||
raise OpenStackCloudException(
|
||||
"Machine encountered a failure.")
|
||||
# NOTE(TheJulia): This performs matching if the requested
|
||||
# end state matches the state the node has reached.
|
||||
if state in machine['provision_state']:
|
||||
break
|
||||
# NOTE(TheJulia): This performs matching for cases where
|
||||
# the reqeusted state action ends in available state.
|
||||
if ("available" in machine['provision_state'] and
|
||||
state in ["provide", "deleted"]):
|
||||
break
|
||||
else:
|
||||
url = '/nodes/{node_id}/states/provision'.format(
|
||||
node_id=name_or_id)
|
||||
payload = {'target': state}
|
||||
if configdrive:
|
||||
payload['configdrive'] = configdrive
|
||||
|
||||
machine = self._baremetal_client.put(url,
|
||||
json=payload,
|
||||
error_message=msg,
|
||||
microversion=version)
|
||||
if wait:
|
||||
for count in _utils._iterate_timeout(
|
||||
timeout,
|
||||
"Timeout waiting for node transition to "
|
||||
"target state of '%s'" % state):
|
||||
machine = self.get_machine(name_or_id)
|
||||
return machine
|
||||
if 'failed' in machine['provision_state']:
|
||||
raise OpenStackCloudException(
|
||||
"Machine encountered a failure.")
|
||||
# NOTE(TheJulia): This performs matching if the requested
|
||||
# end state matches the state the node has reached.
|
||||
if state in machine['provision_state']:
|
||||
break
|
||||
# NOTE(TheJulia): This performs matching for cases where
|
||||
# the reqeusted state action ends in available state.
|
||||
if ("available" in machine['provision_state'] and
|
||||
state in ["provide", "deleted"]):
|
||||
break
|
||||
else:
|
||||
machine = self.get_machine(name_or_id)
|
||||
return machine
|
||||
|
||||
def set_machine_maintenance_state(
|
||||
self,
|
||||
@ -603,25 +678,17 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
|
||||
:returns: None
|
||||
"""
|
||||
with _utils.shade_exceptions(
|
||||
"Error setting machine maintenance state to {state} on node "
|
||||
"{node}".format(state=state, node=name_or_id)
|
||||
):
|
||||
if state:
|
||||
result = self.manager.submit_task(
|
||||
_tasks.MachineSetMaintenance(node_id=name_or_id,
|
||||
state='true',
|
||||
maint_reason=reason))
|
||||
else:
|
||||
result = self.manager.submit_task(
|
||||
_tasks.MachineSetMaintenance(node_id=name_or_id,
|
||||
state='false'))
|
||||
if result is not None:
|
||||
raise OpenStackCloudException(
|
||||
"Failed setting machine maintenance state to %s "
|
||||
"on node %s. Received: %s" % (
|
||||
state, name_or_id, result))
|
||||
return None
|
||||
msg = ("Error setting machine maintenance state to {state} on node "
|
||||
"{node}").format(state=state, node=name_or_id)
|
||||
url = '/nodes/{name_or_id}/maintenance'.format(name_or_id=name_or_id)
|
||||
if state:
|
||||
payload = {'reason': reason}
|
||||
self._baremetal_client.put(url,
|
||||
json=payload,
|
||||
error_message=msg)
|
||||
else:
|
||||
self._baremetal_client.delete(url, error_message=msg)
|
||||
return None
|
||||
|
||||
def remove_machine_from_maintenance(self, name_or_id):
|
||||
"""Remove Baremetal Machine from Maintenance State
|
||||
@ -658,18 +725,19 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
|
||||
:returns: None
|
||||
"""
|
||||
with _utils.shade_exceptions(
|
||||
"Error setting machine power state to {state} on node "
|
||||
"{node}".format(state=state, node=name_or_id)
|
||||
):
|
||||
power = self.manager.submit_task(
|
||||
_tasks.MachineSetPower(node_id=name_or_id,
|
||||
state=state))
|
||||
if power is not None:
|
||||
raise OpenStackCloudException(
|
||||
"Failed setting machine power state %s on node %s. "
|
||||
"Received: %s" % (state, name_or_id, power))
|
||||
return None
|
||||
msg = ("Error setting machine power state to {state} on node "
|
||||
"{node}").format(state=state, node=name_or_id)
|
||||
url = '/nodes/{name_or_id}/states/power'.format(name_or_id=name_or_id)
|
||||
if 'reboot' in state:
|
||||
desired_state = 'rebooting'
|
||||
else:
|
||||
desired_state = 'power {state}'.format(state=state)
|
||||
payload = {'target': desired_state}
|
||||
self._baremetal_client.put(url,
|
||||
json=payload,
|
||||
error_message=msg,
|
||||
microversion="1.6")
|
||||
return None
|
||||
|
||||
def set_machine_power_on(self, name_or_id):
|
||||
"""Activate baremetal machine power
|
||||
@ -729,16 +797,69 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
uuid, 'deleted', wait=wait, timeout=timeout)
|
||||
|
||||
def set_node_instance_info(self, uuid, patch):
|
||||
with _utils.shade_exceptions():
|
||||
return self.manager.submit_task(
|
||||
_tasks.MachineNodeUpdate(node_id=uuid, patch=patch))
|
||||
msg = ("Error updating machine via patch operation on node "
|
||||
"{node}".format(node=uuid))
|
||||
url = '/nodes/{node_id}'.format(node_id=uuid)
|
||||
return self._baremetal_client.patch(url,
|
||||
json=patch,
|
||||
error_message=msg)
|
||||
|
||||
def purge_node_instance_info(self, uuid):
|
||||
patch = []
|
||||
patch.append({'op': 'remove', 'path': '/instance_info'})
|
||||
with _utils.shade_exceptions():
|
||||
return self.manager.submit_task(
|
||||
_tasks.MachineNodeUpdate(node_id=uuid, patch=patch))
|
||||
msg = ("Error updating machine via patch operation on node "
|
||||
"{node}".format(node=uuid))
|
||||
url = '/nodes/{node_id}'.format(node_id=uuid)
|
||||
return self._baremetal_client.patch(url,
|
||||
json=patch,
|
||||
error_message=msg)
|
||||
|
||||
def wait_for_baremetal_node_lock(self, node, timeout=30):
|
||||
"""Wait for a baremetal node to have no lock.
|
||||
|
||||
Baremetal nodes in ironic have a reservation lock that
|
||||
is used to represent that a conductor has locked the node
|
||||
while performing some sort of action, such as changing
|
||||
configuration as a result of a machine state change.
|
||||
|
||||
This lock can occur during power syncronization, and prevents
|
||||
updates to objects attached to the node, such as ports.
|
||||
|
||||
In the vast majority of cases, locks should clear in a few
|
||||
seconds, and as such this method will only wait for 30 seconds.
|
||||
The default wait is two seconds between checking if the lock
|
||||
has cleared.
|
||||
|
||||
This method is intended for use by methods that need to
|
||||
gracefully block without genreating errors, however this
|
||||
method does prevent another client or a timer from
|
||||
triggering a lock immediately after we see the lock as
|
||||
having cleared.
|
||||
|
||||
:param node: The json representation of the node,
|
||||
specificially looking for the node
|
||||
'uuid' and 'reservation' fields.
|
||||
:param timeout: Integer in seconds to wait for the
|
||||
lock to clear. Default: 30
|
||||
|
||||
:raises: OpenStackCloudException upon client failure.
|
||||
:returns: None
|
||||
"""
|
||||
# TODO(TheJulia): This _can_ still fail with a race
|
||||
# condition in that between us checking the status,
|
||||
# a conductor where the conductor could still obtain
|
||||
# a lock before we are able to obtain a lock.
|
||||
# This means we should handle this with such conections
|
||||
|
||||
if node['reservation'] is None:
|
||||
return
|
||||
else:
|
||||
msg = 'Waiting for lock to be released for node {node}'.format(
|
||||
node=node['uuid'])
|
||||
for count in _utils._iterate_timeout(timeout, msg, 2):
|
||||
current_node = self.get_machine(node['uuid'])
|
||||
if current_node['reservation'] is None:
|
||||
return
|
||||
|
||||
@_utils.valid_kwargs('type', 'service_type', 'description')
|
||||
def create_service(self, name, enabled=True, **kwargs):
|
||||
|
@ -358,7 +358,8 @@ class FakeVolumeSnapshot(object):
|
||||
class FakeMachine(object):
|
||||
def __init__(self, id, name=None, driver=None, driver_info=None,
|
||||
chassis_uuid=None, instance_info=None, instance_uuid=None,
|
||||
properties=None, reservation=None, last_error=None):
|
||||
properties=None, reservation=None, last_error=None,
|
||||
provision_state=None):
|
||||
self.uuid = id
|
||||
self.name = name
|
||||
self.driver = driver
|
||||
@ -369,6 +370,7 @@ class FakeMachine(object):
|
||||
self.properties = properties
|
||||
self.reservation = reservation
|
||||
self.last_error = last_error
|
||||
self.provision_state = provision_state
|
||||
|
||||
|
||||
class FakeMachinePort(object):
|
||||
|
@ -672,6 +672,7 @@ class TestBaremetalNode(base.IronicTestCase):
|
||||
|
||||
def test_node_set_provision_state_wait_timeout_fails(self):
|
||||
# Intentionally time out.
|
||||
self.fake_baremetal_node['provision_state'] = 'deploy wait'
|
||||
self.register_uris([
|
||||
dict(
|
||||
method='PUT',
|
||||
@ -780,6 +781,55 @@ class TestBaremetalNode(base.IronicTestCase):
|
||||
self.assertEqual(available_node, return_value)
|
||||
self.assert_calls()
|
||||
|
||||
def test_wait_for_baremetal_node_lock_locked(self):
|
||||
self.fake_baremetal_node['reservation'] = 'conductor0'
|
||||
unlocked_node = self.fake_baremetal_node.copy()
|
||||
unlocked_node['reservation'] = None
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(
|
||||
resource='nodes',
|
||||
append=[self.fake_baremetal_node['uuid']]),
|
||||
json=self.fake_baremetal_node),
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(
|
||||
resource='nodes',
|
||||
append=[self.fake_baremetal_node['uuid']]),
|
||||
json=unlocked_node),
|
||||
])
|
||||
self.assertIsNone(
|
||||
self.op_cloud.wait_for_baremetal_node_lock(
|
||||
self.fake_baremetal_node,
|
||||
timeout=1))
|
||||
|
||||
self.assert_calls()
|
||||
|
||||
def test_wait_for_baremetal_node_lock_not_locked(self):
|
||||
self.fake_baremetal_node['reservation'] = None
|
||||
self.assertIsNone(
|
||||
self.op_cloud.wait_for_baremetal_node_lock(
|
||||
self.fake_baremetal_node,
|
||||
timeout=1))
|
||||
|
||||
self.assertEqual(0, len(self.adapter.request_history))
|
||||
|
||||
def test_wait_for_baremetal_node_lock_timeout(self):
|
||||
self.fake_baremetal_node['reservation'] = 'conductor0'
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(
|
||||
resource='nodes',
|
||||
append=[self.fake_baremetal_node['uuid']]),
|
||||
json=self.fake_baremetal_node),
|
||||
])
|
||||
self.assertRaises(
|
||||
exc.OpenStackCloudException,
|
||||
self.op_cloud.wait_for_baremetal_node_lock,
|
||||
self.fake_baremetal_node,
|
||||
timeout=0.001)
|
||||
|
||||
self.assert_calls()
|
||||
|
||||
def test_activate_node(self):
|
||||
self.fake_baremetal_node['provision_state'] = 'active'
|
||||
self.register_uris([
|
||||
@ -834,9 +884,7 @@ class TestBaremetalNode(base.IronicTestCase):
|
||||
node_uuid = self.fake_baremetal_node['uuid']
|
||||
# TODO(TheJulia): There is a lot of duplication
|
||||
# in testing creation. Surely this hsould be a helper
|
||||
# or something. We should fix this, after we have
|
||||
# ironicclient removed, as in the mean time visibility
|
||||
# will be helpful.
|
||||
# or something. We should fix this.
|
||||
node_to_post = {
|
||||
'chassis_uuid': None,
|
||||
'driver': None,
|
||||
@ -867,11 +915,10 @@ class TestBaremetalNode(base.IronicTestCase):
|
||||
self.assertDictEqual(self.fake_baremetal_node, return_value)
|
||||
self.assert_calls()
|
||||
|
||||
# TODO(TheJulia): After we remove ironicclient,
|
||||
# we need to de-duplicate these tests. Possibly
|
||||
# a dedicated class, although we should do it then
|
||||
# as we may find differences that need to be accounted
|
||||
# for newer microversions.
|
||||
# TODO(TheJulia): We need to de-duplicate these tests.
|
||||
# Possibly a dedicated class, although we should do it
|
||||
# then as we may find differences that need to be
|
||||
# accounted for newer microversions.
|
||||
def test_register_machine_enroll(self):
|
||||
mac_address = '00:01:02:03:04:05'
|
||||
nics = [{'mac': mac_address}]
|
||||
@ -1336,6 +1383,33 @@ class TestBaremetalNode(base.IronicTestCase):
|
||||
|
||||
self.assert_calls()
|
||||
|
||||
def test_unregister_machine_locked_timeout(self):
|
||||
mac_address = self.fake_baremetal_port['address']
|
||||
nics = [{'mac': mac_address}]
|
||||
self.fake_baremetal_node['provision_state'] = 'available'
|
||||
self.fake_baremetal_node['reservation'] = 'conductor99'
|
||||
self.register_uris([
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(
|
||||
resource='nodes',
|
||||
append=[self.fake_baremetal_node['uuid']]),
|
||||
json=self.fake_baremetal_node),
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(
|
||||
resource='nodes',
|
||||
append=[self.fake_baremetal_node['uuid']]),
|
||||
json=self.fake_baremetal_node),
|
||||
])
|
||||
self.assertRaises(
|
||||
exc.OpenStackCloudException,
|
||||
self.op_cloud.unregister_machine,
|
||||
nics,
|
||||
self.fake_baremetal_node['uuid'],
|
||||
timeout=0.001)
|
||||
self.assert_calls()
|
||||
|
||||
def test_unregister_machine_unavailable(self):
|
||||
# This is a list of invalid states that the method
|
||||
# should fail on.
|
||||
|
121
openstack/tests/unit/cloud/test_baremetal_ports.py
Normal file
121
openstack/tests/unit/cloud/test_baremetal_ports.py
Normal file
@ -0,0 +1,121 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
test_baremetal_ports
|
||||
----------------------------------
|
||||
|
||||
Tests for baremetal port related operations
|
||||
"""
|
||||
|
||||
from testscenarios import load_tests_apply_scenarios as load_tests # noqa
|
||||
|
||||
from openstack.cloud import exc
|
||||
from openstack.tests import fakes
|
||||
from openstack.tests.unit import base
|
||||
|
||||
|
||||
class TestBaremetalPort(base.IronicTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestBaremetalPort, self).setUp()
|
||||
self.fake_baremetal_node = fakes.make_fake_machine(
|
||||
self.name, self.uuid)
|
||||
# TODO(TheJulia): Some tests below have fake ports,
|
||||
# since they are required in some processes. Lets refactor
|
||||
# them at some point to use self.fake_baremetal_port.
|
||||
self.fake_baremetal_port = fakes.make_fake_port(
|
||||
'00:01:02:03:04:05',
|
||||
node_id=self.uuid)
|
||||
self.fake_baremetal_port2 = fakes.make_fake_port(
|
||||
'0a:0b:0c:0d:0e:0f',
|
||||
node_id=self.uuid)
|
||||
|
||||
def test_list_nics(self):
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(resource='ports'),
|
||||
json={'ports': [self.fake_baremetal_port,
|
||||
self.fake_baremetal_port2]}),
|
||||
])
|
||||
|
||||
return_value = self.op_cloud.list_nics()
|
||||
self.assertEqual(2, len(return_value['ports']))
|
||||
self.assertEqual(self.fake_baremetal_port, return_value['ports'][0])
|
||||
self.assert_calls()
|
||||
|
||||
def test_list_nics_failure(self):
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(resource='ports'),
|
||||
status_code=400)
|
||||
])
|
||||
self.assertRaises(exc.OpenStackCloudException,
|
||||
self.op_cloud.list_nics)
|
||||
self.assert_calls()
|
||||
|
||||
def test_list_nics_for_machine(self):
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(
|
||||
resource='nodes',
|
||||
append=[self.fake_baremetal_node['uuid'], 'ports']),
|
||||
json={'ports': [self.fake_baremetal_port,
|
||||
self.fake_baremetal_port2]}),
|
||||
])
|
||||
|
||||
return_value = self.op_cloud.list_nics_for_machine(
|
||||
self.fake_baremetal_node['uuid'])
|
||||
self.assertEqual(2, len(return_value['ports']))
|
||||
self.assertEqual(self.fake_baremetal_port, return_value['ports'][0])
|
||||
self.assert_calls()
|
||||
|
||||
def test_list_nics_for_machine_failure(self):
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(
|
||||
resource='nodes',
|
||||
append=[self.fake_baremetal_node['uuid'], 'ports']),
|
||||
status_code=400)
|
||||
])
|
||||
|
||||
self.assertRaises(exc.OpenStackCloudException,
|
||||
self.op_cloud.list_nics_for_machine,
|
||||
self.fake_baremetal_node['uuid'])
|
||||
self.assert_calls()
|
||||
|
||||
def test_get_nic_by_mac(self):
|
||||
mac = self.fake_baremetal_port['address']
|
||||
query = 'detail?address=%s' % mac
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(resource='ports', append=[query]),
|
||||
json={'ports': [self.fake_baremetal_port]}),
|
||||
])
|
||||
|
||||
return_value = self.op_cloud.get_nic_by_mac(mac)
|
||||
|
||||
self.assertEqual(self.fake_baremetal_port, return_value)
|
||||
self.assert_calls()
|
||||
|
||||
def test_get_nic_by_mac_failure(self):
|
||||
mac = self.fake_baremetal_port['address']
|
||||
query = 'detail?address=%s' % mac
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(resource='ports', append=[query]),
|
||||
json={'ports': []}),
|
||||
])
|
||||
|
||||
self.assertIsNone(self.op_cloud.get_nic_by_mac(mac))
|
||||
|
||||
self.assert_calls()
|
@ -15,7 +15,6 @@ import testtools
|
||||
|
||||
import openstack
|
||||
from openstack.cloud import exc
|
||||
from openstack.cloud import meta
|
||||
from openstack.config import cloud_config
|
||||
from openstack.tests import fakes
|
||||
from openstack.tests.unit import base
|
||||
@ -26,37 +25,6 @@ class TestOperatorCloud(base.RequestsMockTestCase):
|
||||
def test_operator_cloud(self):
|
||||
self.assertIsInstance(self.op_cloud, openstack.OperatorCloud)
|
||||
|
||||
@mock.patch.object(openstack.OperatorCloud, 'ironic_client')
|
||||
def test_list_nics(self, mock_client):
|
||||
port1 = fakes.FakeMachinePort(1, "aa:bb:cc:dd", "node1")
|
||||
port2 = fakes.FakeMachinePort(2, "dd:cc:bb:aa", "node2")
|
||||
port_list = [port1, port2]
|
||||
port_dict_list = meta.obj_list_to_munch(port_list)
|
||||
|
||||
mock_client.port.list.return_value = port_list
|
||||
nics = self.op_cloud.list_nics()
|
||||
|
||||
self.assertTrue(mock_client.port.list.called)
|
||||
self.assertEqual(port_dict_list, nics)
|
||||
|
||||
@mock.patch.object(openstack.OperatorCloud, 'ironic_client')
|
||||
def test_list_nics_failure(self, mock_client):
|
||||
mock_client.port.list.side_effect = Exception()
|
||||
self.assertRaises(exc.OpenStackCloudException,
|
||||
self.op_cloud.list_nics)
|
||||
|
||||
@mock.patch.object(openstack.OperatorCloud, 'ironic_client')
|
||||
def test_list_nics_for_machine(self, mock_client):
|
||||
mock_client.node.list_ports.return_value = []
|
||||
self.op_cloud.list_nics_for_machine("123")
|
||||
mock_client.node.list_ports.assert_called_with(node_id="123")
|
||||
|
||||
@mock.patch.object(openstack.OperatorCloud, 'ironic_client')
|
||||
def test_list_nics_for_machine_failure(self, mock_client):
|
||||
mock_client.node.list_ports.side_effect = Exception()
|
||||
self.assertRaises(exc.OpenStackCloudException,
|
||||
self.op_cloud.list_nics_for_machine, None)
|
||||
|
||||
@mock.patch.object(cloud_config.CloudConfig, 'get_endpoint')
|
||||
def test_get_session_endpoint_provided(self, fake_get_endpoint):
|
||||
fake_get_endpoint.return_value = 'http://fake.url'
|
||||
|
@ -12,11 +12,63 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# TODO(shade) Port this content back in from shade repo as tests don't have
|
||||
# references to ironic_client.
|
||||
|
||||
import openstack
|
||||
from openstack.tests.unit import base
|
||||
|
||||
|
||||
class TestShadeOperatorNoAuth(base.RequestsMockTestCase):
|
||||
pass
|
||||
class TestOpenStackCloudOperatorNoAuth(base.RequestsMockTestCase):
|
||||
def setUp(self):
|
||||
"""Setup Noauth OperatorCloud tests
|
||||
|
||||
Setup the test to utilize no authentication and an endpoint
|
||||
URL in the auth data. This is permits testing of the basic
|
||||
mechanism that enables Ironic noauth mode to be utilized with
|
||||
Shade.
|
||||
|
||||
Uses base.RequestsMockTestCase instead of IronicTestCase because
|
||||
we need to do completely different things with discovery.
|
||||
"""
|
||||
super(TestOpenStackCloudOperatorNoAuth, self).setUp()
|
||||
# By clearing the URI registry, we remove all calls to a keystone
|
||||
# catalog or getting a token
|
||||
self._uri_registry.clear()
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(
|
||||
service_type='baremetal', base_url_append='v1',
|
||||
resource='nodes'),
|
||||
json={'nodes': []}),
|
||||
])
|
||||
|
||||
def test_ironic_noauth_none_auth_type(self):
|
||||
"""Test noauth selection for Ironic in OperatorCloud
|
||||
|
||||
The new way of doing this is with the keystoneauth none plugin.
|
||||
"""
|
||||
# NOTE(TheJulia): When we are using the python-ironicclient
|
||||
# library, the library will automatically prepend the URI path
|
||||
# with 'v1'. As such, since we are overriding the endpoint,
|
||||
# we must explicitly do the same as we move away from the
|
||||
# client library.
|
||||
self.cloud_noauth = openstack.operator_cloud(
|
||||
auth_type='none',
|
||||
baremetal_endpoint_override="https://bare-metal.example.com/v1")
|
||||
|
||||
self.cloud_noauth.list_machines()
|
||||
|
||||
self.assert_calls()
|
||||
|
||||
def test_ironic_noauth_admin_token_auth_type(self):
|
||||
"""Test noauth selection for Ironic in OperatorCloud
|
||||
|
||||
The old way of doing this was to abuse admin_token.
|
||||
"""
|
||||
self.cloud_noauth = openstack.operator_cloud(
|
||||
auth_type='admin_token',
|
||||
auth=dict(
|
||||
endpoint='https://bare-metal.example.com/v1',
|
||||
token='ignored'))
|
||||
|
||||
self.cloud_noauth.list_machines()
|
||||
|
||||
self.assert_calls()
|
||||
|
Loading…
x
Reference in New Issue
Block a user