Add update_machine method
Additional method which leverages jsonpatch to appropriately update a machine's configuration as-necessary. Change-Id: I401829be21484519eb4880e7108c7693c9501df8
This commit is contained in:
parent
2318c033e2
commit
6ac348dd13
@ -1,5 +1,6 @@
|
||||
pbr>=0.5.21,<1.0
|
||||
|
||||
jsonpatch
|
||||
os-client-config>=0.7.0
|
||||
six
|
||||
|
||||
|
@ -24,6 +24,7 @@ import glanceclient
|
||||
import glanceclient.exc
|
||||
from ironicclient import client as ironic_client
|
||||
from ironicclient import exceptions as ironic_exceptions
|
||||
import jsonpatch
|
||||
from keystoneclient import auth as ksc_auth
|
||||
from keystoneclient import session as ksc_session
|
||||
from keystoneclient import client as keystone_client
|
||||
@ -2294,6 +2295,100 @@ class OperatorCloud(OpenStackCloud):
|
||||
"Error updating machine via patch operation. node: %s. "
|
||||
"%s" % (name_or_id, e))
|
||||
|
||||
def update_machine(self, name_or_id, chassis_uuid=None, driver=None,
|
||||
driver_info=None, name=None, instance_info=None,
|
||||
instance_uuid=None, properties=None):
|
||||
"""Update a machine with new configuration information
|
||||
|
||||
A user-friendly method to perform updates of a machine, in whole or
|
||||
part.
|
||||
|
||||
:param string name_or_id: A machine name or UUID to be updated.
|
||||
:param string chassis_uuid: Assign a chassis UUID to the machine.
|
||||
NOTE: As of the Kilo release, this value
|
||||
cannot be changed once set. If a user
|
||||
attempts to change this value, then the
|
||||
Ironic API, as of Kilo, will reject the
|
||||
request.
|
||||
:param string driver: The driver name for controlling the machine.
|
||||
:param dict driver_info: The dictonary defining the configuration
|
||||
that the driver will utilize to control
|
||||
the machine. Permutations of this are
|
||||
dependent upon the specific driver utilized.
|
||||
:param string name: A human relatable name to represent the machine.
|
||||
:param dict instance_info: A dictonary of configuration information
|
||||
that conveys to the driver how the host
|
||||
is to be configured when deployed.
|
||||
be deployed to the machine.
|
||||
:param string instance_uuid: A UUID value representing the instance
|
||||
that the deployed machine represents.
|
||||
:param dict properties: A dictonary defining the properties of a
|
||||
machine.
|
||||
|
||||
:raises: OpenStackCloudException on operation error.
|
||||
|
||||
:returns: Dictonary containing a machine sub-dictonary consisting
|
||||
of the updated data returned from the API update operation,
|
||||
and a list named changes which contains all of the API paths
|
||||
that received updates.
|
||||
"""
|
||||
try:
|
||||
machine = self.get_machine(name_or_id)
|
||||
|
||||
machine_config = {}
|
||||
new_config = {}
|
||||
|
||||
if chassis_uuid:
|
||||
machine_config['chassis_uuid'] = machine['chassis_uuid']
|
||||
new_config['chassis_uuid'] = chassis_uuid
|
||||
|
||||
if driver:
|
||||
machine_config['driver'] = machine['driver']
|
||||
new_config['driver'] = driver
|
||||
|
||||
if driver_info:
|
||||
machine_config['driver_info'] = machine['driver_info']
|
||||
new_config['driver_info'] = driver_info
|
||||
|
||||
if name:
|
||||
machine_config['name'] = machine['name']
|
||||
new_config['name'] = name
|
||||
|
||||
if instance_info:
|
||||
machine_config['instance_info'] = machine['instance_info']
|
||||
new_config['instance_info'] = instance_info
|
||||
|
||||
if instance_uuid:
|
||||
machine_config['instance_uuid'] = machine['instance_uuid']
|
||||
new_config['instance_uuid'] = instance_uuid
|
||||
|
||||
if properties:
|
||||
machine_config['properties'] = machine['properties']
|
||||
new_config['properties'] = properties
|
||||
|
||||
patch = jsonpatch.JsonPatch.from_diff(machine_config, new_config)
|
||||
|
||||
if not patch:
|
||||
return dict(
|
||||
node=machine,
|
||||
changes=None
|
||||
)
|
||||
else:
|
||||
machine = self.patch_machine(machine['uuid'], list(patch))
|
||||
change_list = []
|
||||
for change in list(patch):
|
||||
change_list.append(change['path'])
|
||||
return dict(
|
||||
node=machine,
|
||||
changes=change_list
|
||||
)
|
||||
except Exception as e:
|
||||
self.log.debug(
|
||||
"Machine update failed", exc_info=True)
|
||||
raise OpenStackCloudException(
|
||||
"Error updating machine node %s. "
|
||||
"%s" % (name_or_id, e))
|
||||
|
||||
def validate_node(self, uuid):
|
||||
try:
|
||||
ifaces = self.ironic_client.node.validate(uuid)
|
||||
|
@ -211,6 +211,250 @@ class TestShadeOperator(base.TestCase):
|
||||
self.cloud.patch_machine(node_id, patch)
|
||||
self.assertTrue(mock_client.node.update.called)
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
@mock.patch.object(shade.OperatorCloud, 'patch_machine')
|
||||
def test_update_machine_patch_no_action(self, mock_patch, mock_client):
|
||||
class client_return_value:
|
||||
uuid = '00000000-0000-0000-0000-000000000000'
|
||||
name = 'node01'
|
||||
|
||||
expected_machine = dict(
|
||||
uuid='00000000-0000-0000-0000-000000000000',
|
||||
name='node01'
|
||||
)
|
||||
mock_client.node.get.return_value = client_return_value
|
||||
|
||||
update_dict = self.cloud.update_machine('node01')
|
||||
self.assertIsNone(update_dict['changes'])
|
||||
self.assertFalse(mock_patch.called)
|
||||
self.assertDictEqual(expected_machine, update_dict['node'])
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
@mock.patch.object(shade.OperatorCloud, 'patch_machine')
|
||||
def test_update_machine_patch_no_action_name(self, mock_patch,
|
||||
mock_client):
|
||||
class client_return_value:
|
||||
uuid = '00000000-0000-0000-0000-000000000000'
|
||||
name = 'node01'
|
||||
|
||||
expected_machine = dict(
|
||||
uuid='00000000-0000-0000-0000-000000000000',
|
||||
name='node01'
|
||||
)
|
||||
mock_client.node.get.return_value = client_return_value
|
||||
|
||||
update_dict = self.cloud.update_machine('node01', name='node01')
|
||||
self.assertIsNone(update_dict['changes'])
|
||||
self.assertFalse(mock_patch.called)
|
||||
self.assertDictEqual(expected_machine, update_dict['node'])
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
@mock.patch.object(shade.OperatorCloud, 'patch_machine')
|
||||
def test_update_machine_patch_action_name(self, mock_patch,
|
||||
mock_client):
|
||||
class client_return_value:
|
||||
uuid = '00000000-0000-0000-0000-000000000000'
|
||||
name = 'evil'
|
||||
|
||||
expected_patch = [dict(op='replace', path='/name', value='good')]
|
||||
|
||||
mock_client.node.get.return_value = client_return_value
|
||||
|
||||
update_dict = self.cloud.update_machine('evil', name='good')
|
||||
self.assertIsNotNone(update_dict['changes'])
|
||||
self.assertEqual('/name', update_dict['changes'][0])
|
||||
self.assertTrue(mock_patch.called)
|
||||
mock_patch.assert_called_with(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
expected_patch)
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
@mock.patch.object(shade.OperatorCloud, 'patch_machine')
|
||||
def test_update_machine_patch_update_name(self, mock_patch,
|
||||
mock_client):
|
||||
class client_return_value:
|
||||
uuid = '00000000-0000-0000-0000-000000000000'
|
||||
name = 'evil'
|
||||
|
||||
expected_patch = [dict(op='replace', path='/name', value='good')]
|
||||
|
||||
mock_client.node.get.return_value = client_return_value
|
||||
|
||||
update_dict = self.cloud.update_machine('evil', name='good')
|
||||
self.assertIsNotNone(update_dict['changes'])
|
||||
self.assertEqual('/name', update_dict['changes'][0])
|
||||
self.assertTrue(mock_patch.called)
|
||||
mock_patch.assert_called_with(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
expected_patch)
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
@mock.patch.object(shade.OperatorCloud, 'patch_machine')
|
||||
def test_update_machine_patch_update_chassis_uuid(self, mock_patch,
|
||||
mock_client):
|
||||
class client_return_value:
|
||||
uuid = '00000000-0000-0000-0000-000000000000'
|
||||
chassis_uuid = None
|
||||
|
||||
expected_patch = [
|
||||
dict(
|
||||
op='replace',
|
||||
path='/chassis_uuid',
|
||||
value='00000000-0000-0000-0000-000000000001'
|
||||
)]
|
||||
|
||||
mock_client.node.get.return_value = client_return_value
|
||||
|
||||
update_dict = self.cloud.update_machine(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
chassis_uuid='00000000-0000-0000-0000-000000000001')
|
||||
self.assertIsNotNone(update_dict['changes'])
|
||||
self.assertEqual('/chassis_uuid', update_dict['changes'][0])
|
||||
self.assertTrue(mock_patch.called)
|
||||
mock_patch.assert_called_with(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
expected_patch)
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
@mock.patch.object(shade.OperatorCloud, 'patch_machine')
|
||||
def test_update_machine_patch_update_driver(self, mock_patch,
|
||||
mock_client):
|
||||
class client_return_value:
|
||||
uuid = '00000000-0000-0000-0000-000000000000'
|
||||
driver = None
|
||||
|
||||
expected_patch = [
|
||||
dict(
|
||||
op='replace',
|
||||
path='/driver',
|
||||
value='fake'
|
||||
)]
|
||||
|
||||
mock_client.node.get.return_value = client_return_value
|
||||
|
||||
update_dict = self.cloud.update_machine(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
driver='fake'
|
||||
)
|
||||
self.assertIsNotNone(update_dict['changes'])
|
||||
self.assertEqual('/driver', update_dict['changes'][0])
|
||||
self.assertTrue(mock_patch.called)
|
||||
mock_patch.assert_called_with(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
expected_patch)
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
@mock.patch.object(shade.OperatorCloud, 'patch_machine')
|
||||
def test_update_machine_patch_update_driver_info(self, mock_patch,
|
||||
mock_client):
|
||||
class client_return_value:
|
||||
uuid = '00000000-0000-0000-0000-000000000000'
|
||||
driver_info = None
|
||||
|
||||
expected_patch = [
|
||||
dict(
|
||||
op='replace',
|
||||
path='/driver_info',
|
||||
value=dict(var='fake')
|
||||
)]
|
||||
|
||||
mock_client.node.get.return_value = client_return_value
|
||||
|
||||
update_dict = self.cloud.update_machine(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
driver_info=dict(var="fake")
|
||||
)
|
||||
self.assertIsNotNone(update_dict['changes'])
|
||||
self.assertEqual('/driver_info', update_dict['changes'][0])
|
||||
self.assertTrue(mock_patch.called)
|
||||
mock_patch.assert_called_with(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
expected_patch)
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
@mock.patch.object(shade.OperatorCloud, 'patch_machine')
|
||||
def test_update_machine_patch_update_instance_info(self, mock_patch,
|
||||
mock_client):
|
||||
class client_return_value:
|
||||
uuid = '00000000-0000-0000-0000-000000000000'
|
||||
instance_info = None
|
||||
|
||||
expected_patch = [
|
||||
dict(
|
||||
op='replace',
|
||||
path='/instance_info',
|
||||
value=dict(var='fake')
|
||||
)]
|
||||
|
||||
mock_client.node.get.return_value = client_return_value
|
||||
|
||||
update_dict = self.cloud.update_machine(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
instance_info=dict(var="fake")
|
||||
)
|
||||
self.assertIsNotNone(update_dict['changes'])
|
||||
self.assertEqual('/instance_info', update_dict['changes'][0])
|
||||
self.assertTrue(mock_patch.called)
|
||||
mock_patch.assert_called_with(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
expected_patch)
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
@mock.patch.object(shade.OperatorCloud, 'patch_machine')
|
||||
def test_update_machine_patch_update_instance_uuid(self, mock_patch,
|
||||
mock_client):
|
||||
class client_return_value:
|
||||
uuid = '00000000-0000-0000-0000-000000000000'
|
||||
instance_uuid = None
|
||||
|
||||
expected_patch = [
|
||||
dict(
|
||||
op='replace',
|
||||
path='/instance_uuid',
|
||||
value='00000000-0000-0000-0000-000000000002'
|
||||
)]
|
||||
|
||||
mock_client.node.get.return_value = client_return_value
|
||||
|
||||
update_dict = self.cloud.update_machine(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
instance_uuid='00000000-0000-0000-0000-000000000002'
|
||||
)
|
||||
self.assertIsNotNone(update_dict['changes'])
|
||||
self.assertEqual('/instance_uuid', update_dict['changes'][0])
|
||||
self.assertTrue(mock_patch.called)
|
||||
mock_patch.assert_called_with(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
expected_patch)
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
@mock.patch.object(shade.OperatorCloud, 'patch_machine')
|
||||
def test_update_machine_patch_update_properties(self, mock_patch,
|
||||
mock_client):
|
||||
class client_return_value:
|
||||
uuid = '00000000-0000-0000-0000-000000000000'
|
||||
properties = None
|
||||
|
||||
expected_patch = [
|
||||
dict(
|
||||
op='replace',
|
||||
path='/properties',
|
||||
value=dict(var='fake')
|
||||
)]
|
||||
|
||||
mock_client.node.get.return_value = client_return_value
|
||||
|
||||
update_dict = self.cloud.update_machine(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
properties=dict(var="fake")
|
||||
)
|
||||
self.assertIsNotNone(update_dict['changes'])
|
||||
self.assertEqual('/properties', update_dict['changes'][0])
|
||||
self.assertTrue(mock_patch.called)
|
||||
mock_patch.assert_called_with(
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
expected_patch)
|
||||
|
||||
@mock.patch.object(shade.OperatorCloud, 'ironic_client')
|
||||
def test_register_machine(self, mock_client):
|
||||
class fake_node:
|
||||
|
Loading…
x
Reference in New Issue
Block a user