Remove Ironic client wrapper
We no longer need this since everything is using SDK. The removal of this highlights a couple of areas where there were outstanding unused mocks. These are cleaned up in the process. Change-Id: I6247bfbb157b37eff66ac3974bf91780a3d01c1d Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
This commit is contained in:
parent
259b646afa
commit
07db53c213
@ -1,184 +0,0 @@
|
|||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
from ironicclient import client as ironic_client
|
|
||||||
from ironicclient import exc as ironic_exception
|
|
||||||
from keystoneauth1 import discover as ksa_disc
|
|
||||||
import keystoneauth1.session
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
import nova.conf
|
|
||||||
from nova import exception
|
|
||||||
from nova import test
|
|
||||||
from nova.tests.unit.virt.ironic import utils as ironic_utils
|
|
||||||
from nova.virt.ironic import client_wrapper
|
|
||||||
|
|
||||||
CONF = nova.conf.CONF
|
|
||||||
|
|
||||||
FAKE_CLIENT = ironic_utils.FakeClient()
|
|
||||||
|
|
||||||
|
|
||||||
def get_new_fake_client(*args, **kwargs):
|
|
||||||
return ironic_utils.FakeClient()
|
|
||||||
|
|
||||||
|
|
||||||
class IronicClientWrapperTestCase(test.NoDBTestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(IronicClientWrapperTestCase, self).setUp()
|
|
||||||
self.ironicclient = client_wrapper.IronicClientWrapper()
|
|
||||||
# Do not waste time sleeping
|
|
||||||
cfg.CONF.set_override('api_retry_interval', 0, 'ironic')
|
|
||||||
cfg.CONF.set_override('region_name', 'RegionOne', 'ironic')
|
|
||||||
get_ksa_adapter_p = mock.patch('nova.utils.get_ksa_adapter')
|
|
||||||
self.addCleanup(get_ksa_adapter_p.stop)
|
|
||||||
self.get_ksa_adapter = get_ksa_adapter_p.start()
|
|
||||||
get_auth_plugin_p = mock.patch('nova.virt.ironic.client_wrapper.'
|
|
||||||
'IronicClientWrapper._get_auth_plugin')
|
|
||||||
self.addCleanup(get_auth_plugin_p.stop)
|
|
||||||
self.get_auth_plugin = get_auth_plugin_p.start()
|
|
||||||
|
|
||||||
@mock.patch.object(client_wrapper.IronicClientWrapper, '_multi_getattr')
|
|
||||||
@mock.patch.object(client_wrapper.IronicClientWrapper, '_get_client')
|
|
||||||
def test_call_good_no_args(self, mock_get_client, mock_multi_getattr):
|
|
||||||
mock_get_client.return_value = FAKE_CLIENT
|
|
||||||
self.ironicclient.call("node.list")
|
|
||||||
mock_get_client.assert_called_once_with(retry_on_conflict=True)
|
|
||||||
mock_multi_getattr.assert_called_once_with(FAKE_CLIENT, "node.list")
|
|
||||||
mock_multi_getattr.return_value.assert_called_once_with()
|
|
||||||
|
|
||||||
@mock.patch.object(client_wrapper.IronicClientWrapper, '_multi_getattr')
|
|
||||||
@mock.patch.object(client_wrapper.IronicClientWrapper, '_get_client')
|
|
||||||
def test_call_good_with_args(self, mock_get_client, mock_multi_getattr):
|
|
||||||
mock_get_client.return_value = FAKE_CLIENT
|
|
||||||
self.ironicclient.call("node.list", 'test', associated=True)
|
|
||||||
mock_get_client.assert_called_once_with(retry_on_conflict=True)
|
|
||||||
mock_multi_getattr.assert_called_once_with(FAKE_CLIENT, "node.list")
|
|
||||||
mock_multi_getattr.return_value.assert_called_once_with(
|
|
||||||
'test', associated=True)
|
|
||||||
|
|
||||||
@mock.patch.object(keystoneauth1.session, 'Session')
|
|
||||||
@mock.patch.object(ironic_client, 'get_client')
|
|
||||||
def test__get_client_session(self, mock_ir_cli, mock_session):
|
|
||||||
"""An Ironicclient is called with a keystoneauth1 Session"""
|
|
||||||
mock_session.return_value = 'session'
|
|
||||||
ironicclient = client_wrapper.IronicClientWrapper()
|
|
||||||
# dummy call to have _get_client() called
|
|
||||||
ironicclient.call("node.list")
|
|
||||||
self.get_ksa_adapter.assert_called_once_with(
|
|
||||||
'baremetal', ksa_auth=self.get_auth_plugin.return_value,
|
|
||||||
ksa_session='session', min_version=(1, 0),
|
|
||||||
max_version=(1, ksa_disc.LATEST))
|
|
||||||
expected = {'session': 'session',
|
|
||||||
'max_retries': CONF.ironic.api_max_retries,
|
|
||||||
'retry_interval': CONF.ironic.api_retry_interval,
|
|
||||||
'os_ironic_api_version': ['1.46', '1.38'],
|
|
||||||
'endpoint':
|
|
||||||
self.get_ksa_adapter.return_value.get_endpoint.return_value,
|
|
||||||
'interface': ['internal', 'public']}
|
|
||||||
mock_ir_cli.assert_called_once_with(1, **expected)
|
|
||||||
|
|
||||||
@mock.patch.object(keystoneauth1.session, 'Session')
|
|
||||||
@mock.patch.object(ironic_client, 'get_client')
|
|
||||||
def test__get_client_session_service_not_found(self, mock_ir_cli,
|
|
||||||
mock_session):
|
|
||||||
"""Validate behavior when get_endpoint_data raises."""
|
|
||||||
mock_session.return_value = 'session'
|
|
||||||
self.get_ksa_adapter.side_effect = (
|
|
||||||
exception.ConfGroupForServiceTypeNotFound(stype='baremetal'))
|
|
||||||
ironicclient = client_wrapper.IronicClientWrapper()
|
|
||||||
# dummy call to have _get_client() called
|
|
||||||
ironicclient.call("node.list")
|
|
||||||
self.get_ksa_adapter.assert_called_once_with(
|
|
||||||
'baremetal', ksa_auth=self.get_auth_plugin.return_value,
|
|
||||||
ksa_session='session', min_version=(1, 0),
|
|
||||||
max_version=(1, ksa_disc.LATEST))
|
|
||||||
# When get_endpoint_data raises any ServiceNotFound, None is returned.
|
|
||||||
expected = {'session': 'session',
|
|
||||||
'max_retries': CONF.ironic.api_max_retries,
|
|
||||||
'retry_interval': CONF.ironic.api_retry_interval,
|
|
||||||
'os_ironic_api_version': ['1.46', '1.38'],
|
|
||||||
'endpoint': None,
|
|
||||||
'region_name': CONF.ironic.region_name,
|
|
||||||
'interface': ['internal', 'public']}
|
|
||||||
mock_ir_cli.assert_called_once_with(1, **expected)
|
|
||||||
|
|
||||||
@mock.patch.object(keystoneauth1.session, 'Session')
|
|
||||||
@mock.patch.object(ironic_client, 'get_client')
|
|
||||||
def test__get_client_and_valid_interfaces(self, mock_ir_cli, mock_session):
|
|
||||||
"""Confirm explicit setting of valid_interfaces."""
|
|
||||||
mock_session.return_value = 'session'
|
|
||||||
endpoint = 'https://baremetal.example.com/endpoint'
|
|
||||||
self.get_ksa_adapter.return_value.get_endpoint.return_value = endpoint
|
|
||||||
self.flags(endpoint_override=endpoint, group='ironic')
|
|
||||||
self.flags(valid_interfaces='admin', group='ironic')
|
|
||||||
ironicclient = client_wrapper.IronicClientWrapper()
|
|
||||||
# dummy call to have _get_client() called
|
|
||||||
ironicclient.call("node.list")
|
|
||||||
expected = {'session': 'session',
|
|
||||||
'max_retries': CONF.ironic.api_max_retries,
|
|
||||||
'retry_interval': CONF.ironic.api_retry_interval,
|
|
||||||
'os_ironic_api_version': ['1.46', '1.38'],
|
|
||||||
'endpoint': endpoint,
|
|
||||||
'interface': ['admin']}
|
|
||||||
mock_ir_cli.assert_called_once_with(1, **expected)
|
|
||||||
|
|
||||||
@mock.patch.object(client_wrapper.IronicClientWrapper, '_multi_getattr')
|
|
||||||
@mock.patch.object(client_wrapper.IronicClientWrapper, '_get_client')
|
|
||||||
def test_call_fail_exception(self, mock_get_client, mock_multi_getattr):
|
|
||||||
test_obj = mock.Mock()
|
|
||||||
test_obj.side_effect = ironic_exception.HTTPNotFound
|
|
||||||
mock_multi_getattr.return_value = test_obj
|
|
||||||
mock_get_client.return_value = FAKE_CLIENT
|
|
||||||
self.assertRaises(ironic_exception.HTTPNotFound,
|
|
||||||
self.ironicclient.call, "node.list")
|
|
||||||
|
|
||||||
@mock.patch.object(ironic_client, 'get_client')
|
|
||||||
def test__get_client_unauthorized(self, mock_get_client):
|
|
||||||
mock_get_client.side_effect = ironic_exception.Unauthorized
|
|
||||||
self.assertRaises(exception.NovaException,
|
|
||||||
self.ironicclient._get_client)
|
|
||||||
|
|
||||||
@mock.patch.object(ironic_client, 'get_client')
|
|
||||||
def test__get_client_unexpected_exception(self, mock_get_client):
|
|
||||||
mock_get_client.side_effect = ironic_exception.ConnectionRefused
|
|
||||||
self.assertRaises(ironic_exception.ConnectionRefused,
|
|
||||||
self.ironicclient._get_client)
|
|
||||||
|
|
||||||
def test__multi_getattr_good(self):
|
|
||||||
response = self.ironicclient._multi_getattr(FAKE_CLIENT, "node.list")
|
|
||||||
self.assertEqual(FAKE_CLIENT.node.list, response)
|
|
||||||
|
|
||||||
def test__multi_getattr_fail(self):
|
|
||||||
self.assertRaises(AttributeError, self.ironicclient._multi_getattr,
|
|
||||||
FAKE_CLIENT, "nonexistent")
|
|
||||||
|
|
||||||
@mock.patch.object(ironic_client, 'get_client')
|
|
||||||
def test__client_is_cached(self, mock_get_client):
|
|
||||||
mock_get_client.side_effect = get_new_fake_client
|
|
||||||
ironicclient = client_wrapper.IronicClientWrapper()
|
|
||||||
first_client = ironicclient._get_client()
|
|
||||||
second_client = ironicclient._get_client()
|
|
||||||
self.assertEqual(id(first_client), id(second_client))
|
|
||||||
|
|
||||||
@mock.patch.object(ironic_client, 'get_client')
|
|
||||||
def test_call_uses_cached_client(self, mock_get_client):
|
|
||||||
mock_get_client.side_effect = get_new_fake_client
|
|
||||||
ironicclient = client_wrapper.IronicClientWrapper()
|
|
||||||
for n in range(0, 4):
|
|
||||||
ironicclient.call("node.list")
|
|
||||||
self.assertEqual(1, mock_get_client.call_count)
|
|
@ -53,23 +53,15 @@ from nova.virt import configdrive
|
|||||||
from nova.virt import driver
|
from nova.virt import driver
|
||||||
from nova.virt import fake
|
from nova.virt import fake
|
||||||
from nova.virt import hardware
|
from nova.virt import hardware
|
||||||
from nova.virt.ironic import client_wrapper as cw
|
|
||||||
from nova.virt.ironic import driver as ironic_driver
|
from nova.virt.ironic import driver as ironic_driver
|
||||||
from nova.virt.ironic import ironic_states
|
from nova.virt.ironic import ironic_states
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
FAKE_CLIENT = ironic_utils.FakeClient()
|
|
||||||
|
|
||||||
SENTINEL = object()
|
SENTINEL = object()
|
||||||
|
|
||||||
|
|
||||||
class FakeClientWrapper(cw.IronicClientWrapper):
|
|
||||||
def _get_client(self, retry_on_conflict=True):
|
|
||||||
return FAKE_CLIENT
|
|
||||||
|
|
||||||
|
|
||||||
class FakeLoopingCall(object):
|
class FakeLoopingCall(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.wait = mock.MagicMock()
|
self.wait = mock.MagicMock()
|
||||||
@ -104,14 +96,8 @@ def _make_compute_service(hostname):
|
|||||||
return objects.Service(host=hostname)
|
return objects.Service(host=hostname)
|
||||||
|
|
||||||
|
|
||||||
FAKE_CLIENT_WRAPPER = FakeClientWrapper()
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(cw, 'IronicClientWrapper', lambda *_: FAKE_CLIENT_WRAPPER)
|
|
||||||
class IronicDriverTestCase(test.NoDBTestCase):
|
class IronicDriverTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
@mock.patch.object(cw, 'IronicClientWrapper',
|
|
||||||
lambda *_: FAKE_CLIENT_WRAPPER)
|
|
||||||
@mock.patch.object(ironic_driver.IronicDriver, '_refresh_hash_ring')
|
@mock.patch.object(ironic_driver.IronicDriver, '_refresh_hash_ring')
|
||||||
@mock.patch.object(servicegroup, 'API', autospec=True)
|
@mock.patch.object(servicegroup, 'API', autospec=True)
|
||||||
def setUp(self, mock_sg, mock_hash):
|
def setUp(self, mock_sg, mock_hash):
|
||||||
@ -674,27 +660,31 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||||||
self.mock_conn.get_node.return_value = node
|
self.mock_conn.get_node.return_value = node
|
||||||
self.mock_conn.nodes.return_value = iter([node])
|
self.mock_conn.nodes.return_value = iter([node])
|
||||||
|
|
||||||
self.assertTrue(self.driver.node_is_available(node.uuid))
|
result = self.driver.node_is_available(node.uuid)
|
||||||
|
|
||||||
|
self.assertTrue(result)
|
||||||
self.mock_conn.nodes.assert_called_with(
|
self.mock_conn.nodes.assert_called_with(
|
||||||
fields=ironic_driver._NODE_FIELDS)
|
fields=ironic_driver._NODE_FIELDS)
|
||||||
self.assertEqual(0, self.mock_conn.get_node.call_count)
|
self.assertEqual(0, self.mock_conn.get_node.call_count)
|
||||||
|
|
||||||
@mock.patch.object(FAKE_CLIENT.node, 'list')
|
|
||||||
@mock.patch.object(FAKE_CLIENT.node, 'get')
|
|
||||||
@mock.patch.object(objects.InstanceList, 'get_uuids_by_host')
|
@mock.patch.object(objects.InstanceList, 'get_uuids_by_host')
|
||||||
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
|
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
|
||||||
def test_node_is_available_with_cache(self, mock_services, mock_instances,
|
def test_node_is_available_with_cache(self, mock_services, mock_instances):
|
||||||
mock_get, mock_list):
|
|
||||||
node = _get_cached_node()
|
node = _get_cached_node()
|
||||||
mock_get.return_value = node
|
self.mock_conn.nodes.return_value = iter([node])
|
||||||
mock_list.return_value = [node]
|
|
||||||
# populate the cache
|
# populate the cache, ensuring we called the mock once but then don't
|
||||||
|
# call it again
|
||||||
self.driver.get_available_nodes(refresh=True)
|
self.driver.get_available_nodes(refresh=True)
|
||||||
# prove that zero calls are made after populating cache
|
self.mock_conn.nodes.assert_called_once_with(
|
||||||
mock_list.reset_mock()
|
fields=ironic_driver._NODE_FIELDS,
|
||||||
self.assertTrue(self.driver.node_is_available(node.uuid))
|
)
|
||||||
self.assertEqual(0, mock_list.call_count)
|
self.mock_conn.nodes.reset_mock()
|
||||||
self.assertEqual(0, mock_get.call_count)
|
|
||||||
|
result = self.driver.node_is_available(node.uuid)
|
||||||
|
|
||||||
|
self.assertTrue(result)
|
||||||
|
self.mock_conn.nodes.assert_not_called()
|
||||||
|
|
||||||
def test__node_resources_unavailable(self):
|
def test__node_resources_unavailable(self):
|
||||||
node_dicts = [
|
node_dicts = [
|
||||||
@ -1103,18 +1093,18 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||||||
result = self.ptree.data(mock.sentinel.nodename).traits
|
result = self.ptree.data(mock.sentinel.nodename).traits
|
||||||
self.assertEqual(set(traits), result)
|
self.assertEqual(set(traits), result)
|
||||||
|
|
||||||
@mock.patch.object(FAKE_CLIENT.node, 'list')
|
|
||||||
@mock.patch.object(objects.InstanceList, 'get_uuids_by_host')
|
@mock.patch.object(objects.InstanceList, 'get_uuids_by_host')
|
||||||
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
|
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
|
||||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_resource')
|
@mock.patch.object(ironic_driver.IronicDriver, '_node_resource')
|
||||||
def test_get_available_resource(self, mock_nr, mock_services,
|
def test_get_available_resource(
|
||||||
mock_instances, mock_list):
|
self, mock_nr, mock_services, mock_instances,
|
||||||
|
):
|
||||||
node = _get_cached_node()
|
node = _get_cached_node()
|
||||||
node_2 = _get_cached_node(id=uuidutils.generate_uuid())
|
node_2 = _get_cached_node(id=uuidutils.generate_uuid())
|
||||||
fake_resource = 'fake-resource'
|
fake_resource = 'fake-resource'
|
||||||
self.mock_conn.get_node.return_value = node
|
self.mock_conn.get_node.return_value = node
|
||||||
# ensure cache gets populated without the node we want
|
# ensure cache gets populated without the node we want
|
||||||
mock_list.return_value = [node_2]
|
self.mock_conn.nodes.return_value = [node_2]
|
||||||
mock_nr.return_value = fake_resource
|
mock_nr.return_value = fake_resource
|
||||||
|
|
||||||
result = self.driver.get_available_resource(node.id)
|
result = self.driver.get_available_resource(node.id)
|
||||||
@ -1250,6 +1240,21 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||||||
instance_id=instance.uuid,
|
instance_id=instance.uuid,
|
||||||
fields=ironic_driver._NODE_FIELDS)
|
fields=ironic_driver._NODE_FIELDS)
|
||||||
|
|
||||||
|
@mock.patch.object(ironic_driver.LOG, 'error')
|
||||||
|
def test__get_node_list_bad_response(self, mock_error):
|
||||||
|
fake_nodes = [_get_cached_node(),
|
||||||
|
ironic_utils.get_test_node(driver='fake',
|
||||||
|
id=uuidutils.generate_uuid())]
|
||||||
|
self.mock_conn.nodes.side_effect = [iter(fake_nodes), Exception()]
|
||||||
|
|
||||||
|
result = self.driver._get_node_list()
|
||||||
|
mock_error.assert_not_called()
|
||||||
|
self.assertEqual(fake_nodes, result)
|
||||||
|
|
||||||
|
self.assertRaises(exception.VirtDriverNotReady,
|
||||||
|
self.driver._get_node_list)
|
||||||
|
mock_error.assert_called_once()
|
||||||
|
|
||||||
@mock.patch.object(objects.InstanceList, 'get_uuids_by_host')
|
@mock.patch.object(objects.InstanceList, 'get_uuids_by_host')
|
||||||
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
|
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
|
||||||
def test_get_info_http_not_found(self, mock_svc_by_hv,
|
def test_get_info_http_not_found(self, mock_svc_by_hv,
|
||||||
@ -2232,16 +2237,15 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||||||
@mock.patch.object(configdrive, 'required_by')
|
@mock.patch.object(configdrive, 'required_by')
|
||||||
@mock.patch.object(ironic_driver.IronicDriver,
|
@mock.patch.object(ironic_driver.IronicDriver,
|
||||||
'_add_instance_info_to_node')
|
'_add_instance_info_to_node')
|
||||||
@mock.patch.object(FAKE_CLIENT.node, 'get')
|
|
||||||
@mock.patch.object(objects.Instance, 'save')
|
@mock.patch.object(objects.Instance, 'save')
|
||||||
def test_rebuild_with_configdrive_failure(self, mock_save, mock_get,
|
def test_rebuild_with_configdrive_failure(self, mock_save,
|
||||||
mock_add_instance_info,
|
mock_add_instance_info,
|
||||||
mock_required_by,
|
mock_required_by,
|
||||||
mock_configdrive):
|
mock_configdrive):
|
||||||
node_uuid = uuidutils.generate_uuid()
|
node_uuid = uuidutils.generate_uuid()
|
||||||
node = _get_cached_node(
|
node = _get_cached_node(
|
||||||
uuid=node_uuid, instance_uuid=self.instance_uuid)
|
uuid=node_uuid, instance_uuid=self.instance_uuid)
|
||||||
mock_get.return_value = node
|
self.mock_conn.get_node.return_value = node
|
||||||
mock_required_by.return_value = True
|
mock_required_by.return_value = True
|
||||||
mock_configdrive.side_effect = exception.NovaException()
|
mock_configdrive.side_effect = exception.NovaException()
|
||||||
|
|
||||||
@ -2262,15 +2266,14 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||||||
@mock.patch.object(configdrive, 'required_by')
|
@mock.patch.object(configdrive, 'required_by')
|
||||||
@mock.patch.object(ironic_driver.IronicDriver,
|
@mock.patch.object(ironic_driver.IronicDriver,
|
||||||
'_add_instance_info_to_node')
|
'_add_instance_info_to_node')
|
||||||
@mock.patch.object(FAKE_CLIENT.node, 'get')
|
|
||||||
@mock.patch.object(objects.Instance, 'save')
|
@mock.patch.object(objects.Instance, 'save')
|
||||||
def test_rebuild_failures(self, mock_save, mock_get,
|
def test_rebuild_failures(self, mock_save,
|
||||||
mock_add_instance_info,
|
mock_add_instance_info,
|
||||||
mock_required_by, mock_configdrive):
|
mock_required_by, mock_configdrive):
|
||||||
node_uuid = uuidutils.generate_uuid()
|
node_uuid = uuidutils.generate_uuid()
|
||||||
node = _get_cached_node(
|
node = _get_cached_node(
|
||||||
uuid=node_uuid, instance_uuid=self.instance_uuid)
|
uuid=node_uuid, instance_uuid=self.instance_uuid)
|
||||||
mock_get.return_value = node
|
self.mock_conn.get_node.return_value = node
|
||||||
mock_required_by.return_value = False
|
mock_required_by.return_value = False
|
||||||
|
|
||||||
image_meta = ironic_utils.get_test_image_meta()
|
image_meta = ironic_utils.get_test_image_meta()
|
||||||
@ -2289,8 +2292,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||||||
bdms=None, detach_block_devices=None,
|
bdms=None, detach_block_devices=None,
|
||||||
attach_block_devices=None)
|
attach_block_devices=None)
|
||||||
|
|
||||||
@mock.patch.object(FAKE_CLIENT.node, 'get')
|
def test_network_binding_host_id(self):
|
||||||
def test_network_binding_host_id(self, mock_get):
|
|
||||||
node_uuid = uuidutils.generate_uuid()
|
node_uuid = uuidutils.generate_uuid()
|
||||||
hostname = 'ironic-compute'
|
hostname = 'ironic-compute'
|
||||||
instance = fake_instance.fake_instance_obj(self.ctx,
|
instance = fake_instance.fake_instance_obj(self.ctx,
|
||||||
@ -2299,7 +2301,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||||||
node = ironic_utils.get_test_node(uuid=node_uuid,
|
node = ironic_utils.get_test_node(uuid=node_uuid,
|
||||||
instance_uuid=self.instance_uuid,
|
instance_uuid=self.instance_uuid,
|
||||||
network_interface='flat')
|
network_interface='flat')
|
||||||
mock_get.return_value = node
|
self.mock_conn.get_node.return_value = node
|
||||||
host_id = self.driver.network_binding_host_id(self.ctx, instance)
|
host_id = self.driver.network_binding_host_id(self.ctx, instance)
|
||||||
self.assertIsNone(host_id)
|
self.assertIsNone(host_id)
|
||||||
|
|
||||||
@ -2469,23 +2471,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||||||
self.driver.clean_networks_preparation(instance, network_info)
|
self.driver.clean_networks_preparation(instance, network_info)
|
||||||
mock_upvifs.assert_called_once_with(instance, network_info)
|
mock_upvifs.assert_called_once_with(instance, network_info)
|
||||||
|
|
||||||
@mock.patch.object(ironic_driver.LOG, 'error')
|
def test_prepare_for_spawn(self):
|
||||||
def test_ironicclient_bad_response(self, mock_error):
|
|
||||||
fake_nodes = [_get_cached_node(),
|
|
||||||
ironic_utils.get_test_node(driver='fake',
|
|
||||||
id=uuidutils.generate_uuid())]
|
|
||||||
self.mock_conn.nodes.side_effect = [iter(fake_nodes), Exception()]
|
|
||||||
|
|
||||||
result = self.driver._get_node_list()
|
|
||||||
mock_error.assert_not_called()
|
|
||||||
self.assertEqual(fake_nodes, result)
|
|
||||||
|
|
||||||
self.assertRaises(exception.VirtDriverNotReady,
|
|
||||||
self.driver._get_node_list)
|
|
||||||
mock_error.assert_called_once()
|
|
||||||
|
|
||||||
@mock.patch.object(cw.IronicClientWrapper, 'call')
|
|
||||||
def test_prepare_for_spawn(self, mock_call):
|
|
||||||
node = ironic_utils.get_test_node(
|
node = ironic_utils.get_test_node(
|
||||||
driver='fake', instance_uuid=None,
|
driver='fake', instance_uuid=None,
|
||||||
provision_state=ironic_states.AVAILABLE,
|
provision_state=ironic_states.AVAILABLE,
|
||||||
@ -2577,9 +2563,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||||||
@mock.patch.object(ironic_driver.IronicDriver, '_unplug_vifs')
|
@mock.patch.object(ironic_driver.IronicDriver, '_unplug_vifs')
|
||||||
@mock.patch.object(ironic_driver.IronicDriver,
|
@mock.patch.object(ironic_driver.IronicDriver,
|
||||||
'_cleanup_volume_target_info')
|
'_cleanup_volume_target_info')
|
||||||
@mock.patch.object(cw.IronicClientWrapper, 'call')
|
def test__cleanup_deploy_no_remove_ii(self, mock_vol, mock_unvif):
|
||||||
def test__cleanup_deploy_no_remove_ii(self, mock_call, mock_vol,
|
|
||||||
mock_unvif):
|
|
||||||
# TODO(TheJulia): This REALLY should be updated to cover all of the
|
# TODO(TheJulia): This REALLY should be updated to cover all of the
|
||||||
# calls that take place.
|
# calls that take place.
|
||||||
node = ironic_utils.get_test_node(driver='fake')
|
node = ironic_utils.get_test_node(driver='fake')
|
||||||
@ -2588,7 +2572,6 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||||||
self.driver._cleanup_deploy(node, instance, remove_instance_info=False)
|
self.driver._cleanup_deploy(node, instance, remove_instance_info=False)
|
||||||
mock_vol.assert_called_once_with(instance)
|
mock_vol.assert_called_once_with(instance)
|
||||||
mock_unvif.assert_called_once_with(node, instance, None)
|
mock_unvif.assert_called_once_with(node, instance, None)
|
||||||
self.assertFalse(mock_call.called)
|
|
||||||
|
|
||||||
|
|
||||||
class IronicDriverSyncTestCase(IronicDriverTestCase):
|
class IronicDriverSyncTestCase(IronicDriverTestCase):
|
||||||
@ -2725,7 +2708,7 @@ class IronicDriverSyncTestCase(IronicDriverTestCase):
|
|||||||
def test__can_send_version(self, mock_supports_microversion):
|
def test__can_send_version(self, mock_supports_microversion):
|
||||||
mock_supports_microversion.return_value = True
|
mock_supports_microversion.return_value = True
|
||||||
|
|
||||||
version = '%d.%d' % cw.IRONIC_API_VERSION
|
version = '%d.%d' % ironic_driver.IRONIC_API_VERSION
|
||||||
self.assertIsNone(
|
self.assertIsNone(
|
||||||
self.driver._can_send_version(version)
|
self.driver._can_send_version(version)
|
||||||
)
|
)
|
||||||
@ -2735,7 +2718,8 @@ class IronicDriverSyncTestCase(IronicDriverTestCase):
|
|||||||
mock_supports_microversion.return_value = False
|
mock_supports_microversion.return_value = False
|
||||||
|
|
||||||
version = '%d.%d' % (
|
version = '%d.%d' % (
|
||||||
cw.IRONIC_API_VERSION[0], cw.IRONIC_API_VERSION[1] + 1,
|
ironic_driver.IRONIC_API_VERSION[0],
|
||||||
|
ironic_driver.IRONIC_API_VERSION[1] + 1,
|
||||||
)
|
)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exception.IronicAPIVersionNotAvailable,
|
exception.IronicAPIVersionNotAvailable,
|
||||||
@ -2756,8 +2740,6 @@ class IronicDriverSyncTestCase(IronicDriverTestCase):
|
|||||||
class IronicDriverGenerateConfigDriveTestCase(test.NoDBTestCase):
|
class IronicDriverGenerateConfigDriveTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
|
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
|
||||||
@mock.patch.object(cw, 'IronicClientWrapper',
|
|
||||||
lambda *_: FAKE_CLIENT_WRAPPER)
|
|
||||||
def setUp(self, mock_services):
|
def setUp(self, mock_services):
|
||||||
super(IronicDriverGenerateConfigDriveTestCase, self).setUp()
|
super(IronicDriverGenerateConfigDriveTestCase, self).setUp()
|
||||||
self.driver = ironic_driver.IronicDriver(None)
|
self.driver = ironic_driver.IronicDriver(None)
|
||||||
@ -3223,8 +3205,6 @@ class NodeCacheTestCase(test.NoDBTestCase):
|
|||||||
|
|
||||||
|
|
||||||
class IronicDriverConsoleTestCase(test.NoDBTestCase):
|
class IronicDriverConsoleTestCase(test.NoDBTestCase):
|
||||||
@mock.patch.object(cw, 'IronicClientWrapper',
|
|
||||||
lambda *_: FAKE_CLIENT_WRAPPER)
|
|
||||||
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
|
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
|
||||||
def setUp(self, mock_services):
|
def setUp(self, mock_services):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
from openstack.baremetal.v1 import node as _node
|
from openstack.baremetal.v1 import node as _node
|
||||||
|
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.virt.ironic import client_wrapper
|
|
||||||
from nova.virt.ironic import ironic_states
|
from nova.virt.ironic import ironic_states
|
||||||
|
|
||||||
|
|
||||||
@ -190,88 +189,3 @@ def get_test_flavor(**kw):
|
|||||||
def get_test_image_meta(**kw):
|
def get_test_image_meta(**kw):
|
||||||
return objects.ImageMeta.from_dict(
|
return objects.ImageMeta.from_dict(
|
||||||
{'id': kw.get('id', 'cccccccc-cccc-cccc-cccc-cccccccccccc')})
|
{'id': kw.get('id', 'cccccccc-cccc-cccc-cccc-cccccccccccc')})
|
||||||
|
|
||||||
|
|
||||||
class FakeVolumeTargetClient(object):
|
|
||||||
|
|
||||||
def create(self, node_uuid, volume_type, properties, boot_index,
|
|
||||||
volume_id):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete(self, volume_target_id):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FakePortClient(object):
|
|
||||||
|
|
||||||
def list(self, address=None, limit=None, marker=None, sort_key=None,
|
|
||||||
sort_dir=None, detail=False, fields=None, node=None,
|
|
||||||
portgroup=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get(self, port_uuid):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update(self, port_uuid, patch):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FakePortgroupClient(object):
|
|
||||||
|
|
||||||
def list(self, node=None, address=None, limit=None, marker=None,
|
|
||||||
sort_key=None, sort_dir=None, detail=False, fields=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FakeNodeClient(object):
|
|
||||||
|
|
||||||
def list(self, associated=None, maintenance=None, marker=None, limit=None,
|
|
||||||
detail=False, sort_key=None, sort_dir=None, fields=None,
|
|
||||||
provision_state=None, driver=None, resource_class=None,
|
|
||||||
chassis=None):
|
|
||||||
return []
|
|
||||||
|
|
||||||
def get(self, node_uuid, fields=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_by_instance_uuid(self, instance_uuid, fields=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def list_ports(self, node_uuid, detail=False):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_power_state(self, node_uuid, target, soft=False, timeout=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_provision_state(self, node_uuid, state, configdrive=None,
|
|
||||||
cleansteps=None, rescue_password=None,
|
|
||||||
os_ironic_api_version=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update(self, node_uuid, patch):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def validate(self, node_uuid):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def vif_attach(self, node_uuid, port_id):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def vif_detach(self, node_uuid, port_id):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def inject_nmi(self, node_uuid):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def list_volume_targets(self, node_uuid, detail=False):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FakeClient(object):
|
|
||||||
|
|
||||||
node = FakeNodeClient()
|
|
||||||
port = FakePortClient()
|
|
||||||
portgroup = FakePortgroupClient()
|
|
||||||
volume_target = FakeVolumeTargetClient()
|
|
||||||
current_api_version = '%d.%d' % client_wrapper.IRONIC_API_VERSION
|
|
||||||
is_api_version_negotiated = True
|
|
||||||
|
@ -1,198 +0,0 @@
|
|||||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from keystoneauth1 import discover as ks_disc
|
|
||||||
from keystoneauth1 import loading as ks_loading
|
|
||||||
from oslo_log import log as logging
|
|
||||||
from oslo_utils import importutils
|
|
||||||
|
|
||||||
import nova.conf
|
|
||||||
from nova import exception
|
|
||||||
from nova.i18n import _
|
|
||||||
from nova import utils
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
CONF = nova.conf.CONF
|
|
||||||
|
|
||||||
ironic = None
|
|
||||||
|
|
||||||
IRONIC_GROUP = nova.conf.ironic.ironic_group
|
|
||||||
|
|
||||||
# The API version required by the Ironic driver
|
|
||||||
IRONIC_API_VERSION = (1, 46)
|
|
||||||
# NOTE(TheJulia): This version should ALWAYS be the _last_ release
|
|
||||||
# supported version of the API version used by nova. If a feature
|
|
||||||
# needs a higher version to be negotiated to operate properly, then the version
|
|
||||||
# above should be updated, and this version should only be changed
|
|
||||||
# once a cycle to the API version desired for features merging in
|
|
||||||
# that cycle.
|
|
||||||
PRIOR_IRONIC_API_VERSION = (1, 38)
|
|
||||||
|
|
||||||
|
|
||||||
class IronicClientWrapper(object):
|
|
||||||
"""Ironic client wrapper class that encapsulates authentication logic."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Initialise the IronicClientWrapper for use.
|
|
||||||
|
|
||||||
Initialise IronicClientWrapper by loading ironicclient
|
|
||||||
dynamically so that ironicclient is not a dependency for
|
|
||||||
Nova.
|
|
||||||
"""
|
|
||||||
global ironic
|
|
||||||
if ironic is None:
|
|
||||||
ironic = importutils.import_module('ironicclient')
|
|
||||||
self._cached_client = None
|
|
||||||
|
|
||||||
def _get_auth_plugin(self):
|
|
||||||
"""Load an auth plugin from CONF options."""
|
|
||||||
# If an auth plugin name is defined in `auth_type` option of [ironic]
|
|
||||||
# group, register its options and load it.
|
|
||||||
auth_plugin = ks_loading.load_auth_from_conf_options(CONF,
|
|
||||||
IRONIC_GROUP.name)
|
|
||||||
|
|
||||||
return auth_plugin
|
|
||||||
|
|
||||||
def _get_client(self, retry_on_conflict=True):
|
|
||||||
max_retries = CONF.ironic.api_max_retries if retry_on_conflict else 1
|
|
||||||
retry_interval = (CONF.ironic.api_retry_interval
|
|
||||||
if retry_on_conflict else 0)
|
|
||||||
|
|
||||||
# If we've already constructed a valid, authed client, just return
|
|
||||||
# that.
|
|
||||||
if retry_on_conflict and self._cached_client is not None:
|
|
||||||
return self._cached_client
|
|
||||||
|
|
||||||
auth_plugin = self._get_auth_plugin()
|
|
||||||
|
|
||||||
sess = ks_loading.load_session_from_conf_options(CONF,
|
|
||||||
IRONIC_GROUP.name,
|
|
||||||
auth=auth_plugin)
|
|
||||||
|
|
||||||
# Retries for Conflict exception
|
|
||||||
kwargs = {}
|
|
||||||
kwargs['max_retries'] = max_retries
|
|
||||||
kwargs['retry_interval'] = retry_interval
|
|
||||||
# NOTE(TheJulia): The ability for a list of available versions to be
|
|
||||||
# accepted was added in python-ironicclient 2.2.0. The highest
|
|
||||||
# available version will be utilized by the client for the lifetime
|
|
||||||
# of the client.
|
|
||||||
kwargs['os_ironic_api_version'] = [
|
|
||||||
'%d.%d' % IRONIC_API_VERSION, '%d.%d' % PRIOR_IRONIC_API_VERSION]
|
|
||||||
|
|
||||||
ironic_conf = CONF[IRONIC_GROUP.name]
|
|
||||||
# valid_interfaces is a list. ironicclient passes this kwarg through to
|
|
||||||
# ksa, which is set up to handle 'interface' as either a list or a
|
|
||||||
# single value.
|
|
||||||
kwargs['interface'] = ironic_conf.valid_interfaces
|
|
||||||
|
|
||||||
# NOTE(clenimar/efried): by default, the endpoint is taken from the
|
|
||||||
# service catalog. Use `endpoint_override` if you want to override it.
|
|
||||||
try:
|
|
||||||
ksa_adap = utils.get_ksa_adapter(
|
|
||||||
nova.conf.ironic.DEFAULT_SERVICE_TYPE,
|
|
||||||
ksa_auth=auth_plugin, ksa_session=sess,
|
|
||||||
min_version=(IRONIC_API_VERSION[0], 0),
|
|
||||||
max_version=(IRONIC_API_VERSION[0], ks_disc.LATEST))
|
|
||||||
ironic_url = ksa_adap.get_endpoint()
|
|
||||||
ironic_url_none_reason = 'returned None'
|
|
||||||
except exception.ServiceNotFound:
|
|
||||||
# NOTE(efried): No reason to believe service catalog lookup
|
|
||||||
# won't also fail in ironic client init, but this way will
|
|
||||||
# yield the expected exception/behavior.
|
|
||||||
ironic_url = None
|
|
||||||
ironic_url_none_reason = 'raised ServiceNotFound'
|
|
||||||
|
|
||||||
if ironic_url is None:
|
|
||||||
LOG.warning("Could not discover ironic_url via keystoneauth1: "
|
|
||||||
"Adapter.get_endpoint %s", ironic_url_none_reason)
|
|
||||||
# NOTE(eandersson): We pass in region here to make sure
|
|
||||||
# that the Ironic client can make an educated decision when
|
|
||||||
# we don't have a valid endpoint to pass on.
|
|
||||||
kwargs['region_name'] = ironic_conf.region_name
|
|
||||||
|
|
||||||
try:
|
|
||||||
cli = ironic.client.get_client(IRONIC_API_VERSION[0],
|
|
||||||
endpoint=ironic_url,
|
|
||||||
session=sess, **kwargs)
|
|
||||||
# Cache the client so we don't have to reconstruct and
|
|
||||||
# reauthenticate it every time we need it.
|
|
||||||
if retry_on_conflict:
|
|
||||||
self._cached_client = cli
|
|
||||||
|
|
||||||
except ironic.exc.Unauthorized:
|
|
||||||
msg = _("Unable to authenticate Ironic client.")
|
|
||||||
LOG.error(msg)
|
|
||||||
raise exception.NovaException(msg)
|
|
||||||
|
|
||||||
return cli
|
|
||||||
|
|
||||||
def _multi_getattr(self, obj, attr):
|
|
||||||
"""Support nested attribute path for getattr().
|
|
||||||
|
|
||||||
:param obj: Root object.
|
|
||||||
:param attr: Path of final attribute to get. E.g., "a.b.c.d"
|
|
||||||
|
|
||||||
:returns: The value of the final named attribute.
|
|
||||||
:raises: AttributeError will be raised if the path is invalid.
|
|
||||||
"""
|
|
||||||
for attribute in attr.split("."):
|
|
||||||
obj = getattr(obj, attribute)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def call(self, method, *args, **kwargs):
|
|
||||||
"""Call an Ironic client method and retry on stale token.
|
|
||||||
|
|
||||||
:param method: Name of the client method to call as a string.
|
|
||||||
:param args: Client method arguments.
|
|
||||||
:param kwargs: Client method keyword arguments.
|
|
||||||
:param retry_on_conflict: Boolean value. Whether the request should be
|
|
||||||
retried in case of a conflict error
|
|
||||||
(HTTP 409) or not. If retry_on_conflict is
|
|
||||||
False the cached instance of the client
|
|
||||||
won't be used. Defaults to True.
|
|
||||||
"""
|
|
||||||
retry_on_conflict = kwargs.pop('retry_on_conflict', True)
|
|
||||||
|
|
||||||
# authentication retry for token expiration is handled in keystone
|
|
||||||
# session, other retries are handled by ironicclient starting with
|
|
||||||
# 0.8.0
|
|
||||||
client = self._get_client(retry_on_conflict=retry_on_conflict)
|
|
||||||
return self._multi_getattr(client, method)(*args, **kwargs)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_api_version(self):
|
|
||||||
"""Value representing the negotiated API client version.
|
|
||||||
|
|
||||||
This value represents the current negotiated API version that
|
|
||||||
is being utilized by the client to permit the caller to make
|
|
||||||
decisions based upon that version.
|
|
||||||
|
|
||||||
:returns: The highest available negotiatable version or None
|
|
||||||
if a version has not yet been negotiated by the underlying
|
|
||||||
client library.
|
|
||||||
"""
|
|
||||||
return self._get_client().current_api_version
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_api_version_negotiated(self):
|
|
||||||
"""Boolean to indicate if the client version has been negotiated.
|
|
||||||
|
|
||||||
:returns: True if the underlying client library has completed API
|
|
||||||
version negotiation. Otherwise the value returned is
|
|
||||||
False.
|
|
||||||
"""
|
|
||||||
return self._get_client().is_api_version_negotiated
|
|
@ -52,17 +52,16 @@ from nova import utils
|
|||||||
from nova.virt import configdrive
|
from nova.virt import configdrive
|
||||||
from nova.virt import driver as virt_driver
|
from nova.virt import driver as virt_driver
|
||||||
from nova.virt import hardware
|
from nova.virt import hardware
|
||||||
from nova.virt.ironic import client_wrapper
|
|
||||||
from nova.virt.ironic import ironic_states
|
from nova.virt.ironic import ironic_states
|
||||||
from nova.virt.ironic import patcher
|
from nova.virt.ironic import patcher
|
||||||
from nova.virt import netutils
|
from nova.virt import netutils
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
CONF = nova.conf.CONF
|
CONF = nova.conf.CONF
|
||||||
|
|
||||||
|
# The API version required by the Ironic driver
|
||||||
|
IRONIC_API_VERSION = (1, 46)
|
||||||
|
|
||||||
_POWER_STATE_MAP = {
|
_POWER_STATE_MAP = {
|
||||||
ironic_states.POWER_ON: power_state.RUNNING,
|
ironic_states.POWER_ON: power_state.RUNNING,
|
||||||
ironic_states.NOSTATE: power_state.NOSTATE,
|
ironic_states.NOSTATE: power_state.NOSTATE,
|
||||||
@ -575,7 +574,7 @@ class IronicDriver(virt_driver.ComputeDriver):
|
|||||||
|
|
||||||
def _get_hypervisor_version(self):
|
def _get_hypervisor_version(self):
|
||||||
"""Returns the version of the Ironic API service endpoint."""
|
"""Returns the version of the Ironic API service endpoint."""
|
||||||
return client_wrapper.IRONIC_API_VERSION[0]
|
return IRONIC_API_VERSION[0]
|
||||||
|
|
||||||
def instance_exists(self, instance):
|
def instance_exists(self, instance):
|
||||||
"""Checks the existence of an instance.
|
"""Checks the existence of an instance.
|
||||||
|
@ -29,7 +29,7 @@ CONF = nova.conf.CONF
|
|||||||
def create(node):
|
def create(node):
|
||||||
"""Create an instance of the appropriate DriverFields class.
|
"""Create an instance of the appropriate DriverFields class.
|
||||||
|
|
||||||
:param node: a node object returned from ironicclient
|
:param node: a node object returned from openstacksdk
|
||||||
:returns: A GenericDriverFields instance.
|
:returns: A GenericDriverFields instance.
|
||||||
"""
|
"""
|
||||||
return GenericDriverFields(node)
|
return GenericDriverFields(node)
|
||||||
|
@ -11,7 +11,6 @@ fixtures>=3.0.0 # Apache-2.0/BSD
|
|||||||
psycopg2-binary>=2.8 # LGPL/ZPL
|
psycopg2-binary>=2.8 # LGPL/ZPL
|
||||||
PyMySQL>=0.8.0 # MIT License
|
PyMySQL>=0.8.0 # MIT License
|
||||||
python-barbicanclient>=4.5.2 # Apache-2.0
|
python-barbicanclient>=4.5.2 # Apache-2.0
|
||||||
python-ironicclient>=3.0.0 # Apache-2.0
|
|
||||||
requests-mock>=1.2.0 # Apache-2.0
|
requests-mock>=1.2.0 # Apache-2.0
|
||||||
oslotest>=3.8.0 # Apache-2.0
|
oslotest>=3.8.0 # Apache-2.0
|
||||||
stestr>=2.0.0 # Apache-2.0
|
stestr>=2.0.0 # Apache-2.0
|
||||||
|
Loading…
Reference in New Issue
Block a user