baremetal: add support for VIF attach/detach API
Change-Id: Ifb311531e9750502a60afac0961e4919c4f8f1e5
This commit is contained in:
@@ -65,6 +65,14 @@ Chassis Operations
|
|||||||
.. automethod:: openstack.baremetal.v1._proxy.Proxy.find_chassis
|
.. automethod:: openstack.baremetal.v1._proxy.Proxy.find_chassis
|
||||||
.. automethod:: openstack.baremetal.v1._proxy.Proxy.chassis
|
.. automethod:: openstack.baremetal.v1._proxy.Proxy.chassis
|
||||||
|
|
||||||
|
VIF Operations
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
.. autoclass:: openstack.baremetal.v1._proxy.Proxy
|
||||||
|
|
||||||
|
.. automethod:: openstack.baremetal.v1._proxy.Proxy.attach_vif_to_node
|
||||||
|
.. automethod:: openstack.baremetal.v1._proxy.Proxy.detach_vif_from_node
|
||||||
|
.. automethod:: openstack.baremetal.v1._proxy.Proxy.list_node_vifs
|
||||||
|
|
||||||
Deprecated Methods
|
Deprecated Methods
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|||||||
@@ -52,3 +52,6 @@ STATE_VERSIONS = {
|
|||||||
'manageable': '1.4',
|
'manageable': '1.4',
|
||||||
}
|
}
|
||||||
"""API versions when certain states were introduced."""
|
"""API versions when certain states were introduced."""
|
||||||
|
|
||||||
|
VIF_VERSION = '1.28'
|
||||||
|
"""API version in which the VIF operations were introduced."""
|
||||||
|
|||||||
@@ -702,3 +702,58 @@ class Proxy(proxy.Proxy):
|
|||||||
"""
|
"""
|
||||||
return self._delete(_portgroup.PortGroup, port_group,
|
return self._delete(_portgroup.PortGroup, port_group,
|
||||||
ignore_missing=ignore_missing)
|
ignore_missing=ignore_missing)
|
||||||
|
|
||||||
|
def attach_vif_to_node(self, node, vif_id):
|
||||||
|
"""Attach a VIF to the node.
|
||||||
|
|
||||||
|
The exact form of the VIF ID depends on the network interface used by
|
||||||
|
the node. In the most common case it is a Network service port
|
||||||
|
(NOT a Bare Metal port) ID. A VIF can only be attached to one node
|
||||||
|
at a time.
|
||||||
|
|
||||||
|
:param node: The value can be either the name or ID of a node or
|
||||||
|
a :class:`~openstack.baremetal.v1.node.Node` instance.
|
||||||
|
:param string vif_id: Backend-specific VIF ID.
|
||||||
|
:return: ``None``
|
||||||
|
:raises: :exc:`~openstack.exceptions.NotSupported` if the server
|
||||||
|
does not support the VIF API.
|
||||||
|
"""
|
||||||
|
res = self._get_resource(_node.Node, node)
|
||||||
|
res.attach_vif(self, vif_id)
|
||||||
|
|
||||||
|
def detach_vif_from_node(self, node, vif_id, ignore_missing=True):
|
||||||
|
"""Detach a VIF from the node.
|
||||||
|
|
||||||
|
The exact form of the VIF ID depends on the network interface used by
|
||||||
|
the node. In the most common case it is a Network service port
|
||||||
|
(NOT a Bare Metal port) ID.
|
||||||
|
|
||||||
|
:param node: The value can be either the name or ID of a node or
|
||||||
|
a :class:`~openstack.baremetal.v1.node.Node` instance.
|
||||||
|
:param string vif_id: Backend-specific VIF ID.
|
||||||
|
:param bool ignore_missing: When set to ``False``
|
||||||
|
:class:`~openstack.exceptions.ResourceNotFound` will be
|
||||||
|
raised when the VIF does not exist. Otherwise, ``False``
|
||||||
|
is returned.
|
||||||
|
:return: ``True`` if the VIF was detached, otherwise ``False``.
|
||||||
|
:raises: :exc:`~openstack.exceptions.NotSupported` if the server
|
||||||
|
does not support the VIF API.
|
||||||
|
"""
|
||||||
|
res = self._get_resource(_node.Node, node)
|
||||||
|
return res.detach_vif(self, vif_id, ignore_missing=ignore_missing)
|
||||||
|
|
||||||
|
def list_node_vifs(self, node):
|
||||||
|
"""List IDs of VIFs attached to the node.
|
||||||
|
|
||||||
|
The exact form of the VIF ID depends on the network interface used by
|
||||||
|
the node. In the most common case it is a Network service port
|
||||||
|
(NOT a Bare Metal port) ID.
|
||||||
|
|
||||||
|
:param node: The value can be either the name or ID of a node or
|
||||||
|
a :class:`~openstack.baremetal.v1.node.Node` instance.
|
||||||
|
:return: List of VIF IDs as strings.
|
||||||
|
:raises: :exc:`~openstack.exceptions.NotSupported` if the server
|
||||||
|
does not support the VIF API.
|
||||||
|
"""
|
||||||
|
res = self._get_resource(_node.Node, node)
|
||||||
|
return res.list_vifs(self)
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ class Node(resource.Resource):
|
|||||||
is_maintenance='maintenance',
|
is_maintenance='maintenance',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Full port groups support introduced in 1.24
|
# VIF attach/detach support introduced in 1.28.
|
||||||
_max_microversion = '1.24'
|
_max_microversion = '1.28'
|
||||||
|
|
||||||
# Properties
|
# Properties
|
||||||
#: The UUID of the chassis associated wit this node. Can be empty or None.
|
#: The UUID of the chassis associated wit this node. Can be empty or None.
|
||||||
@@ -341,6 +341,107 @@ class Node(resource.Resource):
|
|||||||
"the last error is %(error)s" %
|
"the last error is %(error)s" %
|
||||||
{'node': self.id, 'error': self.last_error})
|
{'node': self.id, 'error': self.last_error})
|
||||||
|
|
||||||
|
def attach_vif(self, session, vif_id):
|
||||||
|
"""Attach a VIF to the node.
|
||||||
|
|
||||||
|
The exact form of the VIF ID depends on the network interface used by
|
||||||
|
the node. In the most common case it is a Network service port
|
||||||
|
(NOT a Bare Metal port) ID. A VIF can only be attached to one node
|
||||||
|
at a time.
|
||||||
|
|
||||||
|
:param session: The session to use for making this request.
|
||||||
|
:type session: :class:`~keystoneauth1.adapter.Adapter`
|
||||||
|
:param string vif_id: Backend-specific VIF ID.
|
||||||
|
:return: ``None``
|
||||||
|
:raises: :exc:`~openstack.exceptions.NotSupported` if the server
|
||||||
|
does not support the VIF API.
|
||||||
|
"""
|
||||||
|
session = self._get_session(session)
|
||||||
|
version = self._assert_microversion_for(
|
||||||
|
session, 'commit', _common.VIF_VERSION,
|
||||||
|
error_message=("Cannot use VIF attachment API"))
|
||||||
|
|
||||||
|
request = self._prepare_request(requires_id=True)
|
||||||
|
request.url = utils.urljoin(request.url, 'vifs')
|
||||||
|
body = {'id': vif_id}
|
||||||
|
response = session.post(
|
||||||
|
request.url, json=body,
|
||||||
|
headers=request.headers, microversion=version,
|
||||||
|
# NOTE(dtantsur): do not retry CONFLICT, it's a valid status code
|
||||||
|
# in this API when the VIF is already attached to another node.
|
||||||
|
retriable_status_codes=[503])
|
||||||
|
|
||||||
|
msg = ("Failed to attach VIF {vif} to bare metal node {node}"
|
||||||
|
.format(node=self.id, vif=vif_id))
|
||||||
|
exceptions.raise_from_response(response, error_message=msg)
|
||||||
|
|
||||||
|
def detach_vif(self, session, vif_id, ignore_missing=True):
|
||||||
|
"""Detach a VIF from the node.
|
||||||
|
|
||||||
|
The exact form of the VIF ID depends on the network interface used by
|
||||||
|
the node. In the most common case it is a Network service port
|
||||||
|
(NOT a Bare Metal port) ID.
|
||||||
|
|
||||||
|
:param session: The session to use for making this request.
|
||||||
|
:type session: :class:`~keystoneauth1.adapter.Adapter`
|
||||||
|
:param string vif_id: Backend-specific VIF ID.
|
||||||
|
:param bool ignore_missing: When set to ``False``
|
||||||
|
:class:`~openstack.exceptions.ResourceNotFound` will be
|
||||||
|
raised when the VIF does not exist. Otherwise, ``False``
|
||||||
|
is returned.
|
||||||
|
:return: ``True`` if the VIF was detached, otherwise ``False``.
|
||||||
|
:raises: :exc:`~openstack.exceptions.NotSupported` if the server
|
||||||
|
does not support the VIF API.
|
||||||
|
"""
|
||||||
|
session = self._get_session(session)
|
||||||
|
version = self._assert_microversion_for(
|
||||||
|
session, 'commit', _common.VIF_VERSION,
|
||||||
|
error_message=("Cannot use VIF attachment API"))
|
||||||
|
|
||||||
|
request = self._prepare_request(requires_id=True)
|
||||||
|
request.url = utils.urljoin(request.url, 'vifs', vif_id)
|
||||||
|
response = session.delete(
|
||||||
|
request.url, headers=request.headers, microversion=version,
|
||||||
|
retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
|
||||||
|
|
||||||
|
if ignore_missing and response.status_code == 400:
|
||||||
|
_logger.debug('VIF %(vif)s was already removed from node %(node)s',
|
||||||
|
{'vif': vif_id, 'node': self.id})
|
||||||
|
return False
|
||||||
|
|
||||||
|
msg = ("Failed to detach VIF {vif} from bare metal node {node}"
|
||||||
|
.format(node=self.id, vif=vif_id))
|
||||||
|
exceptions.raise_from_response(response, error_message=msg)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def list_vifs(self, session):
|
||||||
|
"""List IDs of VIFs attached to the node.
|
||||||
|
|
||||||
|
The exact form of the VIF ID depends on the network interface used by
|
||||||
|
the node. In the most common case it is a Network service port
|
||||||
|
(NOT a Bare Metal port) ID.
|
||||||
|
|
||||||
|
:param session: The session to use for making this request.
|
||||||
|
:type session: :class:`~keystoneauth1.adapter.Adapter`
|
||||||
|
:return: List of VIF IDs as strings.
|
||||||
|
:raises: :exc:`~openstack.exceptions.NotSupported` if the server
|
||||||
|
does not support the VIF API.
|
||||||
|
"""
|
||||||
|
session = self._get_session(session)
|
||||||
|
version = self._assert_microversion_for(
|
||||||
|
session, 'fetch', _common.VIF_VERSION,
|
||||||
|
error_message=("Cannot use VIF attachment API"))
|
||||||
|
|
||||||
|
request = self._prepare_request(requires_id=True)
|
||||||
|
request.url = utils.urljoin(request.url, 'vifs')
|
||||||
|
response = session.get(
|
||||||
|
request.url, headers=request.headers, microversion=version)
|
||||||
|
|
||||||
|
msg = ("Failed to list VIFs attached to bare metal node {node}"
|
||||||
|
.format(node=self.id))
|
||||||
|
exceptions.raise_from_response(response, error_message=msg)
|
||||||
|
return [vif['id'] for vif in response.json()['vifs']]
|
||||||
|
|
||||||
|
|
||||||
class NodeDetail(Node):
|
class NodeDetail(Node):
|
||||||
|
|
||||||
|
|||||||
@@ -9821,6 +9821,40 @@ class OpenStackCloud(_normalize.Normalizer):
|
|||||||
changes=change_list
|
changes=change_list
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def attach_port_to_machine(self, name_or_id, port_name_or_id):
|
||||||
|
"""Attach a virtual port to the bare metal machine.
|
||||||
|
|
||||||
|
:param string name_or_id: A machine name or UUID.
|
||||||
|
:param string port_name_or_id: A port name or UUID.
|
||||||
|
Note that this is a Network service port, not a bare metal NIC.
|
||||||
|
:return: Nothing.
|
||||||
|
"""
|
||||||
|
machine = self.get_machine(name_or_id)
|
||||||
|
port = self.get_port(port_name_or_id)
|
||||||
|
self.baremetal.attach_vif_to_node(machine, port['id'])
|
||||||
|
|
||||||
|
def detach_port_from_machine(self, name_or_id, port_name_or_id):
|
||||||
|
"""Detach a virtual port from the bare metal machine.
|
||||||
|
|
||||||
|
:param string name_or_id: A machine name or UUID.
|
||||||
|
:param string port_name_or_id: A port name or UUID.
|
||||||
|
Note that this is a Network service port, not a bare metal NIC.
|
||||||
|
:return: Nothing.
|
||||||
|
"""
|
||||||
|
machine = self.get_machine(name_or_id)
|
||||||
|
port = self.get_port(port_name_or_id)
|
||||||
|
self.baremetal.detach_vif_from_node(machine, port['id'])
|
||||||
|
|
||||||
|
def list_ports_attached_to_machine(self, name_or_id):
|
||||||
|
"""List virtual ports attached to the bare metal machine.
|
||||||
|
|
||||||
|
:param string name_or_id: A machine name or UUID.
|
||||||
|
:returns: List of ``munch.Munch`` representing the ports.
|
||||||
|
"""
|
||||||
|
machine = self.get_machine(name_or_id)
|
||||||
|
vif_ids = self.baremetal.list_node_vifs(machine)
|
||||||
|
return [self.get_port(vif) for vif in vif_ids]
|
||||||
|
|
||||||
def validate_node(self, uuid):
|
def validate_node(self, uuid):
|
||||||
# TODO(TheJulia): There are soooooo many other interfaces
|
# TODO(TheJulia): There are soooooo many other interfaces
|
||||||
# that we can support validating, while these are essential,
|
# that we can support validating, while these are essential,
|
||||||
|
|||||||
@@ -68,3 +68,34 @@ class TestBareMetalNode(base.BaseBaremetalTest):
|
|||||||
ignore_missing=False)
|
ignore_missing=False)
|
||||||
self.assertIsNone(self.conn.baremetal.find_node(uuid))
|
self.assertIsNone(self.conn.baremetal.find_node(uuid))
|
||||||
self.assertIsNone(self.conn.baremetal.delete_node(uuid))
|
self.assertIsNone(self.conn.baremetal.delete_node(uuid))
|
||||||
|
|
||||||
|
|
||||||
|
class TestBareMetalVif(base.BaseBaremetalTest):
|
||||||
|
|
||||||
|
min_microversion = '1.28'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestBareMetalVif, self).setUp()
|
||||||
|
self.node = self.create_node(network_interface='noop')
|
||||||
|
self.vif_id = "200712fc-fdfb-47da-89a6-2d19f76c7618"
|
||||||
|
|
||||||
|
def test_node_vif_attach_detach(self):
|
||||||
|
self.conn.baremetal.attach_vif_to_node(self.node, self.vif_id)
|
||||||
|
# NOTE(dtantsur): The noop networking driver is completely noop - the
|
||||||
|
# VIF list does not return anything of value.
|
||||||
|
self.conn.baremetal.list_node_vifs(self.node)
|
||||||
|
res = self.conn.baremetal.detach_vif_from_node(self.node, self.vif_id,
|
||||||
|
ignore_missing=False)
|
||||||
|
self.assertTrue(res)
|
||||||
|
|
||||||
|
def test_node_vif_negative(self):
|
||||||
|
uuid = "5c9dcd04-2073-49bc-9618-99ae634d8971"
|
||||||
|
self.assertRaises(exceptions.NotFoundException,
|
||||||
|
self.conn.baremetal.attach_vif_to_node,
|
||||||
|
uuid, self.vif_id)
|
||||||
|
self.assertRaises(exceptions.NotFoundException,
|
||||||
|
self.conn.baremetal.list_node_vifs,
|
||||||
|
uuid)
|
||||||
|
self.assertRaises(exceptions.NotFoundException,
|
||||||
|
self.conn.baremetal.detach_vif_from_node,
|
||||||
|
uuid, self.vif_id, ignore_missing=False)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
from keystoneauth1 import adapter
|
from keystoneauth1 import adapter
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
from openstack.baremetal.v1 import _common
|
||||||
from openstack.baremetal.v1 import node
|
from openstack.baremetal.v1 import node
|
||||||
from openstack import exceptions
|
from openstack import exceptions
|
||||||
from openstack.tests.unit import base
|
from openstack.tests.unit import base
|
||||||
@@ -371,3 +372,63 @@ class TestNodeCreate(base.TestCase):
|
|||||||
headers=mock.ANY, microversion=self.session.default_microversion)
|
headers=mock.ANY, microversion=self.session.default_microversion)
|
||||||
mock_prov.assert_called_once_with(self.node, self.session, 'manage',
|
mock_prov.assert_called_once_with(self.node, self.session, 'manage',
|
||||||
wait=True)
|
wait=True)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(exceptions, 'raise_from_response', mock.Mock())
|
||||||
|
@mock.patch.object(node.Node, '_get_session', lambda self, x: x)
|
||||||
|
class TestNodeVif(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestNodeVif, self).setUp()
|
||||||
|
self.session = mock.Mock(spec=adapter.Adapter)
|
||||||
|
self.session.default_microversion = '1.28'
|
||||||
|
self.node = node.Node(id='c29db401-b6a7-4530-af8e-20a720dee946',
|
||||||
|
driver=FAKE['driver'])
|
||||||
|
self.vif_id = '714bdf6d-2386-4b5e-bd0d-bc036f04b1ef'
|
||||||
|
|
||||||
|
def test_attach_vif(self):
|
||||||
|
self.assertIsNone(self.node.attach_vif(self.session, self.vif_id))
|
||||||
|
self.session.post.assert_called_once_with(
|
||||||
|
'nodes/%s/vifs' % self.node.id, json={'id': self.vif_id},
|
||||||
|
headers=mock.ANY, microversion='1.28',
|
||||||
|
retriable_status_codes=[503])
|
||||||
|
|
||||||
|
def test_detach_vif_existing(self):
|
||||||
|
self.assertTrue(self.node.detach_vif(self.session, self.vif_id))
|
||||||
|
self.session.delete.assert_called_once_with(
|
||||||
|
'nodes/%s/vifs/%s' % (self.node.id, self.vif_id),
|
||||||
|
headers=mock.ANY, microversion='1.28',
|
||||||
|
retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
|
||||||
|
|
||||||
|
def test_detach_vif_missing(self):
|
||||||
|
self.session.delete.return_value.status_code = 400
|
||||||
|
self.assertFalse(self.node.detach_vif(self.session, self.vif_id))
|
||||||
|
self.session.delete.assert_called_once_with(
|
||||||
|
'nodes/%s/vifs/%s' % (self.node.id, self.vif_id),
|
||||||
|
headers=mock.ANY, microversion='1.28',
|
||||||
|
retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
|
||||||
|
|
||||||
|
def test_list_vifs(self):
|
||||||
|
self.session.get.return_value.json.return_value = {
|
||||||
|
'vifs': [
|
||||||
|
{'id': '1234'},
|
||||||
|
{'id': '5678'},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
res = self.node.list_vifs(self.session)
|
||||||
|
self.assertEqual(['1234', '5678'], res)
|
||||||
|
self.session.get.assert_called_once_with(
|
||||||
|
'nodes/%s/vifs' % self.node.id,
|
||||||
|
headers=mock.ANY, microversion='1.28')
|
||||||
|
|
||||||
|
def test_incompatible_microversion(self):
|
||||||
|
self.session.default_microversion = '1.1'
|
||||||
|
self.assertRaises(exceptions.NotSupported,
|
||||||
|
self.node.attach_vif,
|
||||||
|
self.session, self.vif_id)
|
||||||
|
self.assertRaises(exceptions.NotSupported,
|
||||||
|
self.node.detach_vif,
|
||||||
|
self.session, self.vif_id)
|
||||||
|
self.assertRaises(exceptions.NotSupported,
|
||||||
|
self.node.list_vifs,
|
||||||
|
self.session)
|
||||||
|
|||||||
@@ -689,7 +689,8 @@ class IronicTestCase(TestCase):
|
|||||||
self.uuid = str(uuid.uuid4())
|
self.uuid = str(uuid.uuid4())
|
||||||
self.name = self.getUniqueString('name')
|
self.name = self.getUniqueString('name')
|
||||||
|
|
||||||
def get_mock_url(self, resource=None, append=None, qs_elements=None):
|
def get_mock_url(self, **kwargs):
|
||||||
return super(IronicTestCase, self).get_mock_url(
|
kwargs.setdefault('service_type', 'baremetal')
|
||||||
service_type='baremetal', interface='public', resource=resource,
|
kwargs.setdefault('interface', 'public')
|
||||||
append=append, base_url_append='v1', qs_elements=qs_elements)
|
kwargs.setdefault('base_url_append', 'v1')
|
||||||
|
return super(IronicTestCase, self).get_mock_url(**kwargs)
|
||||||
|
|||||||
@@ -1609,6 +1609,88 @@ class TestBaremetalNode(base.IronicTestCase):
|
|||||||
|
|
||||||
self.assert_calls()
|
self.assert_calls()
|
||||||
|
|
||||||
|
def test_attach_port_to_machine(self):
|
||||||
|
vif_id = '953ccbee-e854-450f-95fe-fe5e40d611ec'
|
||||||
|
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(
|
||||||
|
service_type='network',
|
||||||
|
resource='ports.json',
|
||||||
|
base_url_append='v2.0'),
|
||||||
|
json={'ports': [{'id': vif_id}]}),
|
||||||
|
dict(
|
||||||
|
method='POST',
|
||||||
|
uri=self.get_mock_url(
|
||||||
|
resource='nodes',
|
||||||
|
append=[self.fake_baremetal_node['uuid'], 'vifs'])),
|
||||||
|
])
|
||||||
|
self.cloud.attach_port_to_machine(self.fake_baremetal_node['uuid'],
|
||||||
|
vif_id)
|
||||||
|
self.assert_calls()
|
||||||
|
|
||||||
|
def test_detach_port_from_machine(self):
|
||||||
|
vif_id = '953ccbee-e854-450f-95fe-fe5e40d611ec'
|
||||||
|
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(
|
||||||
|
service_type='network',
|
||||||
|
resource='ports.json',
|
||||||
|
base_url_append='v2.0'),
|
||||||
|
json={'ports': [{'id': vif_id}]}),
|
||||||
|
dict(
|
||||||
|
method='DELETE',
|
||||||
|
uri=self.get_mock_url(
|
||||||
|
resource='nodes',
|
||||||
|
append=[self.fake_baremetal_node['uuid'], 'vifs',
|
||||||
|
vif_id])),
|
||||||
|
])
|
||||||
|
self.cloud.detach_port_from_machine(self.fake_baremetal_node['uuid'],
|
||||||
|
vif_id)
|
||||||
|
self.assert_calls()
|
||||||
|
|
||||||
|
def test_list_ports_attached_to_machine(self):
|
||||||
|
vif_id = '953ccbee-e854-450f-95fe-fe5e40d611ec'
|
||||||
|
fake_port = {'id': vif_id, 'name': 'test'}
|
||||||
|
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'], 'vifs']),
|
||||||
|
json={'vifs': [{'id': vif_id}]}),
|
||||||
|
dict(
|
||||||
|
method='GET',
|
||||||
|
uri=self.get_mock_url(
|
||||||
|
service_type='network',
|
||||||
|
resource='ports.json',
|
||||||
|
base_url_append='v2.0'),
|
||||||
|
json={'ports': [fake_port]}),
|
||||||
|
])
|
||||||
|
res = self.cloud.list_ports_attached_to_machine(
|
||||||
|
self.fake_baremetal_node['uuid'])
|
||||||
|
self.assert_calls()
|
||||||
|
self.assertEqual([fake_port], res)
|
||||||
|
|
||||||
|
|
||||||
class TestUpdateMachinePatch(base.IronicTestCase):
|
class TestUpdateMachinePatch(base.IronicTestCase):
|
||||||
# NOTE(TheJulia): As appears, and mordred describes,
|
# NOTE(TheJulia): As appears, and mordred describes,
|
||||||
|
|||||||
4
releasenotes/notes/baremetal-vif-122457118c722a9b.yaml
Normal file
4
releasenotes/notes/baremetal-vif-122457118c722a9b.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Implements VIF attach/detach API for bare metal nodes.
|
||||||
Reference in New Issue
Block a user