Volume Target support for Ironic on OpenStack SDK
This patch adds support for Ironic Volume Target API. Change-Id: Ic1e080cfc2c6439fddbb41bc6015abcb59291667 Story: #2008169 Task: #40924
This commit is contained in:
parent
256e25e321
commit
06db9e37fa
@ -69,6 +69,14 @@ Volume Connector Operations
|
|||||||
create_volume_connector, update_volume_connector,
|
create_volume_connector, update_volume_connector,
|
||||||
patch_volume_connector, delete_volume_connector
|
patch_volume_connector, delete_volume_connector
|
||||||
|
|
||||||
|
Volume Target Operations
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
.. autoclass:: openstack.baremetal.v1._proxy.Proxy
|
||||||
|
:noindex:
|
||||||
|
:members: volume_targets, find_volume_target, get_volume_target,
|
||||||
|
create_volume_target, update_volume_target,
|
||||||
|
patch_volume_target, delete_volume_target
|
||||||
|
|
||||||
Utilities
|
Utilities
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -11,3 +11,4 @@ Baremetal Resources
|
|||||||
v1/port_group
|
v1/port_group
|
||||||
v1/allocation
|
v1/allocation
|
||||||
v1/volume_connector
|
v1/volume_connector
|
||||||
|
v1/volume_target
|
||||||
|
13
doc/source/user/resources/baremetal/v1/volume_target.rst
Normal file
13
doc/source/user/resources/baremetal/v1/volume_target.rst
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
openstack.baremetal.v1.volume_target
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
.. automodule:: openstack.baremetal.v1.volume_target
|
||||||
|
|
||||||
|
The VolumeTarget Class
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
The ``VolumeTarget`` class inherits
|
||||||
|
from :class:`~openstack.resource.Resource`.
|
||||||
|
|
||||||
|
.. autoclass:: openstack.baremetal.v1.volume_target.VolumeTarget
|
||||||
|
:members:
|
@ -18,6 +18,7 @@ from openstack.baremetal.v1 import node as _node
|
|||||||
from openstack.baremetal.v1 import port as _port
|
from openstack.baremetal.v1 import port as _port
|
||||||
from openstack.baremetal.v1 import port_group as _portgroup
|
from openstack.baremetal.v1 import port_group as _portgroup
|
||||||
from openstack.baremetal.v1 import volume_connector as _volumeconnector
|
from openstack.baremetal.v1 import volume_connector as _volumeconnector
|
||||||
|
from openstack.baremetal.v1 import volume_target as _volumetarget
|
||||||
from openstack import exceptions
|
from openstack import exceptions
|
||||||
from openstack import proxy
|
from openstack import proxy
|
||||||
from openstack import utils
|
from openstack import utils
|
||||||
@ -231,7 +232,7 @@ class Proxy(proxy.Proxy):
|
|||||||
provided as the ``sort_key``.
|
provided as the ``sort_key``.
|
||||||
* ``sort_key``: Sorts the response by the this attribute value.
|
* ``sort_key``: Sorts the response by the this attribute value.
|
||||||
Default is ``id``. You can specify multiple pairs of sort
|
Default is ``id``. You can specify multiple pairs of sort
|
||||||
key and sort direction query parameters. If you omit the
|
key and sort direction query pa rameters. If you omit the
|
||||||
sort direction in a pair, the API uses the natural sorting
|
sort direction in a pair, the API uses the natural sorting
|
||||||
direction of the server attribute that is provided as the
|
direction of the server attribute that is provided as the
|
||||||
``sort_key``.
|
``sort_key``.
|
||||||
@ -1166,3 +1167,145 @@ class Proxy(proxy.Proxy):
|
|||||||
"""
|
"""
|
||||||
return self._delete(_volumeconnector.VolumeConnector,
|
return self._delete(_volumeconnector.VolumeConnector,
|
||||||
volume_connector, ignore_missing=ignore_missing)
|
volume_connector, ignore_missing=ignore_missing)
|
||||||
|
|
||||||
|
def volume_targets(self, details=False, **query):
|
||||||
|
"""Retrieve a generator of volume_target.
|
||||||
|
|
||||||
|
:param details: A boolean indicating whether the detailed information
|
||||||
|
for every volume_target should be returned.
|
||||||
|
:param dict query: Optional query parameters to be sent to restrict
|
||||||
|
the volume_targets returned. Available parameters include:
|
||||||
|
|
||||||
|
* ``fields``: A list containing one or more fields to be returned
|
||||||
|
in the response. This may lead to some performance gain
|
||||||
|
because other fields of the resource are not refreshed.
|
||||||
|
* ``limit``: Requests at most the specified number of
|
||||||
|
volume_connector be returned from the query.
|
||||||
|
* ``marker``: Specifies the ID of the last-seen volume_target.
|
||||||
|
Use the ``limit`` parameter to make an initial limited request
|
||||||
|
and use the ID of the last-seen volume_target from the
|
||||||
|
response as the ``marker`` value in subsequent limited request.
|
||||||
|
* ``node``:only return the ones associated with this specific node
|
||||||
|
(name or UUID), or an empty set if not found.
|
||||||
|
* ``sort_dir``:Sorts the response by the requested sort direction.
|
||||||
|
A valid value is ``asc`` (ascending) or ``desc``
|
||||||
|
(descending). Default is ``asc``. You can specify multiple
|
||||||
|
pairs of sort key and sort direction query parameters. If
|
||||||
|
you omit the sort direction in a pair, the API uses the
|
||||||
|
natural sorting direction of the server attribute that is
|
||||||
|
provided as the ``sort_key``.
|
||||||
|
* ``sort_key``: Sorts the response by the this attribute value.
|
||||||
|
Default is ``id``. You can specify multiple pairs of sort
|
||||||
|
key and sort direction query parameters. If you omit the
|
||||||
|
sort direction in a pair, the API uses the natural sorting
|
||||||
|
direction of the server attribute that is provided as the
|
||||||
|
``sort_key``.
|
||||||
|
|
||||||
|
:returns: A generator of volume_target instances.
|
||||||
|
"""
|
||||||
|
if details:
|
||||||
|
query['detail'] = True
|
||||||
|
return _volumetarget.VolumeTarget.list(self, **query)
|
||||||
|
|
||||||
|
def create_volume_target(self, **attrs):
|
||||||
|
"""Create a new volume_target from attributes.
|
||||||
|
|
||||||
|
:param dict attrs: Keyword arguments that will be used to create a
|
||||||
|
:class:
|
||||||
|
`~openstack.baremetal.v1.volume_target.VolumeTarget`.
|
||||||
|
|
||||||
|
:returns: The results of volume_target creation.
|
||||||
|
:rtype::class:
|
||||||
|
`~openstack.baremetal.v1.volume_target.VolumeTarget`.
|
||||||
|
"""
|
||||||
|
return self._create(_volumetarget.VolumeTarget, **attrs)
|
||||||
|
|
||||||
|
def find_volume_target(self, vt_id, ignore_missing=True):
|
||||||
|
"""Find a single volume target.
|
||||||
|
|
||||||
|
:param str vt_id: The ID of a volume target.
|
||||||
|
|
||||||
|
:param bool ignore_missing: When set to ``False``, an exception of
|
||||||
|
:class:`~openstack.exceptions.ResourceNotFound` will be raised
|
||||||
|
when the volume connector does not exist. When set to `True``,
|
||||||
|
None will be returned when attempting to find a nonexistent
|
||||||
|
volume target.
|
||||||
|
:returns: One :class:
|
||||||
|
`~openstack.baremetal.v1.volumetarget.VolumeTarget`
|
||||||
|
object or None.
|
||||||
|
"""
|
||||||
|
return self._find(_volumetarget.VolumeTarget, vt_id,
|
||||||
|
ignore_missing=ignore_missing)
|
||||||
|
|
||||||
|
def get_volume_target(self, volume_target, fields=None):
|
||||||
|
"""Get a specific volume_target.
|
||||||
|
|
||||||
|
:param volume_target: The value can be the ID of a
|
||||||
|
volume_target or a :class:
|
||||||
|
`~openstack.baremetal.v1.volume_target.VolumeTarget
|
||||||
|
instance.`
|
||||||
|
:param fields: Limit the resource fields to fetch.`
|
||||||
|
|
||||||
|
:returns: One
|
||||||
|
:class:
|
||||||
|
`~openstack.baremetal.v1.volume_target.VolumeTarget`
|
||||||
|
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no
|
||||||
|
volume_target matching the name or ID could be found.`
|
||||||
|
"""
|
||||||
|
return self._get_with_fields(_volumetarget.VolumeTarget,
|
||||||
|
volume_target,
|
||||||
|
fields=fields)
|
||||||
|
|
||||||
|
def update_volume_target(self, volume_target, **attrs):
|
||||||
|
"""Update a volume_target.
|
||||||
|
|
||||||
|
:param volume_target:Either the ID of a volume_target
|
||||||
|
or an instance of
|
||||||
|
:class:`~openstack.baremetal.v1.volume_target.VolumeTarget.`
|
||||||
|
:param dict attrs: The attributes to update on the
|
||||||
|
volume_target represented by the ``volume_target`` parameter.`
|
||||||
|
|
||||||
|
:returns: The updated volume_target.
|
||||||
|
:rtype::class:
|
||||||
|
`~openstack.baremetal.v1.volume_target.VolumeTarget.`
|
||||||
|
"""
|
||||||
|
return self._update(_volumetarget.VolumeTarget,
|
||||||
|
volume_target, **attrs)
|
||||||
|
|
||||||
|
def patch_volume_target(self, volume_target, patch):
|
||||||
|
"""Apply a JSON patch to the volume_target.
|
||||||
|
|
||||||
|
:param volume_target: The value can be the ID of a
|
||||||
|
volume_target or a :class:
|
||||||
|
`~openstack.baremetal.v1.volume_target.VolumeTarget`
|
||||||
|
instance.
|
||||||
|
:param patch: JSON patch to apply.
|
||||||
|
|
||||||
|
:returns: The updated volume_target.
|
||||||
|
:rtype::class:
|
||||||
|
`~openstack.baremetal.v1.volume_target.VolumeTarget.`
|
||||||
|
"""
|
||||||
|
return self._get_resource(_volumetarget.VolumeTarget,
|
||||||
|
volume_target).patch(self, patch)
|
||||||
|
|
||||||
|
def delete_volume_target(self, volume_target,
|
||||||
|
ignore_missing=True):
|
||||||
|
"""Delete an volume_target.
|
||||||
|
|
||||||
|
:param volume_target: The value can be either the ID of a
|
||||||
|
volume_target.VolumeTarget or a
|
||||||
|
:class:
|
||||||
|
`~openstack.baremetal.v1.volume_target.VolumeTarget`
|
||||||
|
instance.
|
||||||
|
:param bool ignore_missing: When set to ``False``, an exception
|
||||||
|
:class:`~openstack.exceptions.ResourceNotFound` will be raised
|
||||||
|
when the volume_target could not be found.
|
||||||
|
When set to ``True``, no exception will be raised when
|
||||||
|
attempting to delete a non-existent volume_target.
|
||||||
|
|
||||||
|
:returns: The instance of the volume_target which was deleted.
|
||||||
|
:rtype::class:
|
||||||
|
`~openstack.baremetal.v1.volume_target.VolumeTarget`.
|
||||||
|
"""
|
||||||
|
return self._delete(_volumetarget.VolumeTarget,
|
||||||
|
volume_target, ignore_missing=ignore_missing)
|
||||||
|
60
openstack/baremetal/v1/volume_target.py
Normal file
60
openstack/baremetal/v1/volume_target.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# 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 openstack.baremetal.v1 import _common
|
||||||
|
from openstack import resource
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeTarget(_common.ListMixin, resource.Resource):
|
||||||
|
|
||||||
|
resources_key = 'targets'
|
||||||
|
base_path = '/volume/targets'
|
||||||
|
|
||||||
|
# capabilities
|
||||||
|
allow_create = True
|
||||||
|
allow_fetch = True
|
||||||
|
allow_commit = True
|
||||||
|
allow_delete = True
|
||||||
|
allow_list = True
|
||||||
|
allow_patch = True
|
||||||
|
commit_method = 'PATCH'
|
||||||
|
commit_jsonpatch = True
|
||||||
|
|
||||||
|
_query_mapping = resource.QueryParameters(
|
||||||
|
'node', 'detail',
|
||||||
|
fields={'type': _common.fields_type},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Volume Targets is available since 1.32
|
||||||
|
_max_microversion = '1.32'
|
||||||
|
|
||||||
|
#: The boot index of the Volume target. “0” indicates that this volume is
|
||||||
|
# used as a boot volume.
|
||||||
|
boot_index = resource.Body('boot_index')
|
||||||
|
#: Timestamp at which the port was created.
|
||||||
|
created_at = resource.Body('created_at')
|
||||||
|
#: A set of one or more arbitrary metadata key and value pairs.
|
||||||
|
extra = resource.Body('extra')
|
||||||
|
#: A list of relative links. Includes the self and bookmark links.
|
||||||
|
links = resource.Body('links', type=list)
|
||||||
|
#: The UUID of the Node this resource belongs to.
|
||||||
|
node_id = resource.Body('node_uuid')
|
||||||
|
#: A set of physical information of the volume.
|
||||||
|
properties = resource.Body('properties')
|
||||||
|
#: Timestamp at which the port was last updated.
|
||||||
|
updated_at = resource.Body('updated_at')
|
||||||
|
#: The UUID of the resource.
|
||||||
|
id = resource.Body('uuid', alternate_id=True)
|
||||||
|
#: The identifier of the volume.
|
||||||
|
volume_id = resource.Body('volume_id')
|
||||||
|
#: The type of Volume target.
|
||||||
|
volume_type = resource.Body('volume_type')
|
@ -73,3 +73,14 @@ class BaseBaremetalTest(base.BaseFunctionalTest):
|
|||||||
self.conn.baremetal.delete_volume_connector(volume_connector.id,
|
self.conn.baremetal.delete_volume_connector(volume_connector.id,
|
||||||
ignore_missing=True))
|
ignore_missing=True))
|
||||||
return volume_connector
|
return volume_connector
|
||||||
|
|
||||||
|
def create_volume_target(self, node_id=None, **kwargs):
|
||||||
|
node_id = node_id or self.node_id
|
||||||
|
volume_target = self.conn.baremetal.create_volume_target(
|
||||||
|
node_uuid=node_id, **kwargs)
|
||||||
|
|
||||||
|
self.addCleanup(
|
||||||
|
lambda:
|
||||||
|
self.conn.baremetal.delete_volume_target(volume_target.id,
|
||||||
|
ignore_missing=True))
|
||||||
|
return volume_target
|
||||||
|
@ -0,0 +1,179 @@
|
|||||||
|
# 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 openstack import exceptions
|
||||||
|
from openstack.tests.functional.baremetal import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestBareMetalVolumetarget(base.BaseBaremetalTest):
|
||||||
|
|
||||||
|
min_microversion = '1.32'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestBareMetalVolumetarget, self).setUp()
|
||||||
|
self.node = self.create_node(provision_state='enroll')
|
||||||
|
|
||||||
|
def test_volume_target_create_get_delete(self):
|
||||||
|
self.conn.baremetal.set_node_provision_state(
|
||||||
|
self.node, 'manage', wait=True)
|
||||||
|
self.conn.baremetal.set_node_power_state(self.node, 'power off')
|
||||||
|
volume_target = self.create_volume_target(
|
||||||
|
boot_index=0,
|
||||||
|
volume_id='04452bed-5367-4202-8bf5-de4335ac56d2',
|
||||||
|
volume_type='iscsi')
|
||||||
|
|
||||||
|
loaded = self.conn.baremetal.get_volume_target(
|
||||||
|
volume_target.id)
|
||||||
|
self.assertEqual(loaded.id, volume_target.id)
|
||||||
|
self.assertIsNotNone(loaded.node_id)
|
||||||
|
|
||||||
|
with_fields = self.conn.baremetal.get_volume_target(
|
||||||
|
volume_target.id, fields=['uuid', 'extra'])
|
||||||
|
self.assertEqual(volume_target.id, with_fields.id)
|
||||||
|
self.assertIsNone(with_fields.node_id)
|
||||||
|
|
||||||
|
self.conn.baremetal.delete_volume_target(volume_target,
|
||||||
|
ignore_missing=False)
|
||||||
|
self.assertRaises(exceptions.ResourceNotFound,
|
||||||
|
self.conn.baremetal.get_volume_target,
|
||||||
|
volume_target.id)
|
||||||
|
|
||||||
|
def test_volume_target_list(self):
|
||||||
|
node2 = self.create_node(name='test-node')
|
||||||
|
self.conn.baremetal.set_node_provision_state(
|
||||||
|
node2, 'manage', wait=True)
|
||||||
|
self.conn.baremetal.set_node_power_state(node2, 'power off')
|
||||||
|
self.conn.baremetal.set_node_provision_state(
|
||||||
|
self.node, 'manage', wait=True)
|
||||||
|
self.conn.baremetal.set_node_power_state(self.node, 'power off')
|
||||||
|
vt1 = self.create_volume_target(
|
||||||
|
boot_index=0,
|
||||||
|
volume_id='bd4d008c-7d31-463d-abf9-6c23d9d55f7f',
|
||||||
|
node_id=node2.id,
|
||||||
|
volume_type='iscsi')
|
||||||
|
vt2 = self.create_volume_target(
|
||||||
|
boot_index=0,
|
||||||
|
volume_id='04452bed-5367-4202-8bf5-de4335ac57c2',
|
||||||
|
node_id=self.node.id,
|
||||||
|
volume_type='iscsi')
|
||||||
|
|
||||||
|
vts = self.conn.baremetal.volume_targets(
|
||||||
|
node=self.node.id)
|
||||||
|
self.assertEqual([v.id for v in vts], [vt2.id])
|
||||||
|
|
||||||
|
vts = self.conn.baremetal.volume_targets(node=node2.id)
|
||||||
|
self.assertEqual([v.id for v in vts], [vt1.id])
|
||||||
|
|
||||||
|
vts = self.conn.baremetal.volume_targets(node='test-node')
|
||||||
|
self.assertEqual([v.id for v in vts], [vt1.id])
|
||||||
|
|
||||||
|
vts_with_details = self.conn.baremetal.volume_targets(details=True)
|
||||||
|
for i in vts_with_details:
|
||||||
|
self.assertIsNotNone(i.id)
|
||||||
|
self.assertIsNotNone(i.volume_type)
|
||||||
|
|
||||||
|
vts_with_fields = self.conn.baremetal.volume_targets(
|
||||||
|
fields=['uuid', 'node_uuid'])
|
||||||
|
for i in vts_with_fields:
|
||||||
|
self.assertIsNotNone(i.id)
|
||||||
|
self.assertIsNone(i.volume_type)
|
||||||
|
self.assertIsNotNone(i.node_id)
|
||||||
|
|
||||||
|
def test_volume_target_list_update_delete(self):
|
||||||
|
self.conn.baremetal.set_node_provision_state(
|
||||||
|
self.node, 'manage', wait=True)
|
||||||
|
self.conn.baremetal.set_node_power_state(self.node, 'power off')
|
||||||
|
self.create_volume_target(
|
||||||
|
boot_index=0,
|
||||||
|
volume_id='04452bed-5367-4202-8bf5-de4335ac57h3',
|
||||||
|
node_id=self.node.id,
|
||||||
|
volume_type='iscsi',
|
||||||
|
extra={'foo': 'bar'})
|
||||||
|
volume_target = next(self.conn.baremetal.volume_targets(
|
||||||
|
details=True,
|
||||||
|
node=self.node.id))
|
||||||
|
self.assertEqual(volume_target.extra, {'foo': 'bar'})
|
||||||
|
|
||||||
|
# This test checks that resources returned from listing are usable
|
||||||
|
self.conn.baremetal.update_volume_target(volume_target,
|
||||||
|
extra={'foo': 42})
|
||||||
|
self.conn.baremetal.delete_volume_target(volume_target,
|
||||||
|
ignore_missing=False)
|
||||||
|
|
||||||
|
def test_volume_target_update(self):
|
||||||
|
self.conn.baremetal.set_node_provision_state(
|
||||||
|
self.node, 'manage', wait=True)
|
||||||
|
self.conn.baremetal.set_node_power_state(self.node, 'power off')
|
||||||
|
volume_target = self.create_volume_target(
|
||||||
|
boot_index=0,
|
||||||
|
volume_id='04452bed-5367-4202-8bf5-de4335ac53h7',
|
||||||
|
node_id=self.node.id,
|
||||||
|
volume_type='isci')
|
||||||
|
volume_target.extra = {'answer': 42}
|
||||||
|
|
||||||
|
volume_target = self.conn.baremetal.update_volume_target(
|
||||||
|
volume_target)
|
||||||
|
self.assertEqual({'answer': 42}, volume_target.extra)
|
||||||
|
|
||||||
|
volume_target = self.conn.baremetal.get_volume_target(
|
||||||
|
volume_target.id)
|
||||||
|
self.assertEqual({'answer': 42}, volume_target.extra)
|
||||||
|
|
||||||
|
def test_volume_target_patch(self):
|
||||||
|
vol_targ_id = '04452bed-5367-4202-9cg6-de4335ac53h7'
|
||||||
|
self.conn.baremetal.set_node_provision_state(
|
||||||
|
self.node, 'manage', wait=True)
|
||||||
|
self.conn.baremetal.set_node_power_state(self.node, 'power off')
|
||||||
|
volume_target = self.create_volume_target(
|
||||||
|
boot_index=0,
|
||||||
|
volume_id=vol_targ_id,
|
||||||
|
node_id=self.node.id,
|
||||||
|
volume_type='isci')
|
||||||
|
|
||||||
|
volume_target = self.conn.baremetal.patch_volume_target(
|
||||||
|
volume_target, dict(path='/extra/answer', op='add', value=42))
|
||||||
|
self.assertEqual({'answer': 42}, volume_target.extra)
|
||||||
|
self.assertEqual(vol_targ_id,
|
||||||
|
volume_target.volume_id)
|
||||||
|
|
||||||
|
volume_target = self.conn.baremetal.get_volume_target(
|
||||||
|
volume_target.id)
|
||||||
|
self.assertEqual({'answer': 42}, volume_target.extra)
|
||||||
|
|
||||||
|
def test_volume_target_negative_non_existing(self):
|
||||||
|
uuid = "5c9dcd04-2073-49bc-9618-99ae634d8971"
|
||||||
|
self.assertRaises(exceptions.ResourceNotFound,
|
||||||
|
self.conn.baremetal.get_volume_target, uuid)
|
||||||
|
self.assertRaises(exceptions.ResourceNotFound,
|
||||||
|
self.conn.baremetal.find_volume_target, uuid,
|
||||||
|
ignore_missing=False)
|
||||||
|
self.assertRaises(exceptions.ResourceNotFound,
|
||||||
|
self.conn.baremetal.delete_volume_target, uuid,
|
||||||
|
ignore_missing=False)
|
||||||
|
self.assertIsNone(self.conn.baremetal.find_volume_target(uuid))
|
||||||
|
self.assertIsNone(self.conn.baremetal.delete_volume_target(uuid))
|
||||||
|
|
||||||
|
def test_volume_target_fields(self):
|
||||||
|
self.create_node()
|
||||||
|
self.conn.baremetal.set_node_provision_state(
|
||||||
|
self.node, 'manage', wait=True)
|
||||||
|
self.conn.baremetal.set_node_power_state(self.node, 'power off')
|
||||||
|
self.create_volume_target(
|
||||||
|
boot_index=0,
|
||||||
|
volume_id='04452bed-5367-4202-8bf5-99ae634d8971',
|
||||||
|
node_id=self.node.id,
|
||||||
|
volume_type='iscsi')
|
||||||
|
result = self.conn.baremetal.volume_targets(
|
||||||
|
fields=['uuid', 'node_id'])
|
||||||
|
for item in result:
|
||||||
|
self.assertIsNotNone(item.id)
|
@ -20,6 +20,7 @@ from openstack.baremetal.v1 import node
|
|||||||
from openstack.baremetal.v1 import port
|
from openstack.baremetal.v1 import port
|
||||||
from openstack.baremetal.v1 import port_group
|
from openstack.baremetal.v1 import port_group
|
||||||
from openstack.baremetal.v1 import volume_connector
|
from openstack.baremetal.v1 import volume_connector
|
||||||
|
from openstack.baremetal.v1 import volume_target
|
||||||
from openstack import exceptions
|
from openstack import exceptions
|
||||||
from openstack.tests.unit import base
|
from openstack.tests.unit import base
|
||||||
from openstack.tests.unit import test_proxy_base
|
from openstack.tests.unit import test_proxy_base
|
||||||
@ -205,6 +206,42 @@ class TestBaremetalProxy(test_proxy_base.TestProxyBase):
|
|||||||
volume_connector.VolumeConnector,
|
volume_connector.VolumeConnector,
|
||||||
True)
|
True)
|
||||||
|
|
||||||
|
@mock.patch.object(volume_target.VolumeTarget, 'list')
|
||||||
|
def test_volume_target_detailed(self, mock_list):
|
||||||
|
result = self.proxy.volume_targets(details=True, query=1)
|
||||||
|
self.assertIs(result, mock_list.return_value)
|
||||||
|
mock_list.assert_called_once_with(self.proxy, detail=True, query=1)
|
||||||
|
|
||||||
|
@mock.patch.object(volume_target.VolumeTarget, 'list')
|
||||||
|
def test_volume_target_not_detailed(self, mock_list):
|
||||||
|
result = self.proxy.volume_targets(query=1)
|
||||||
|
self.assertIs(result, mock_list.return_value)
|
||||||
|
mock_list.assert_called_once_with(self.proxy, query=1)
|
||||||
|
|
||||||
|
def test_create_volume_target(self):
|
||||||
|
self.verify_create(self.proxy.create_volume_target,
|
||||||
|
volume_target.VolumeTarget)
|
||||||
|
|
||||||
|
def test_find_volume_target(self):
|
||||||
|
self.verify_find(self.proxy.find_volume_target,
|
||||||
|
volume_target.VolumeTarget)
|
||||||
|
|
||||||
|
def test_get_volume_target(self):
|
||||||
|
self.verify_get(self.proxy.get_volume_target,
|
||||||
|
volume_target.VolumeTarget,
|
||||||
|
mock_method=_MOCK_METHOD,
|
||||||
|
expected_kwargs={'fields': None})
|
||||||
|
|
||||||
|
def test_delete_volume_target(self):
|
||||||
|
self.verify_delete(self.proxy.delete_volume_target,
|
||||||
|
volume_target.VolumeTarget,
|
||||||
|
False)
|
||||||
|
|
||||||
|
def test_delete_volume_target_ignore(self):
|
||||||
|
self.verify_delete(self.proxy.delete_volume_target,
|
||||||
|
volume_target.VolumeTarget,
|
||||||
|
True)
|
||||||
|
|
||||||
@mock.patch.object(node.Node, 'fetch', autospec=True)
|
@mock.patch.object(node.Node, 'fetch', autospec=True)
|
||||||
def test__get_with_fields_none(self, mock_fetch):
|
def test__get_with_fields_none(self, mock_fetch):
|
||||||
result = self.proxy._get_with_fields(node.Node, 'value')
|
result = self.proxy._get_with_fields(node.Node, 'value')
|
||||||
|
65
openstack/tests/unit/baremetal/v1/test_volume_target.py
Normal file
65
openstack/tests/unit/baremetal/v1/test_volume_target.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# 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 openstack.tests.unit import base
|
||||||
|
|
||||||
|
from openstack.baremetal.v1 import volume_target
|
||||||
|
|
||||||
|
FAKE = {
|
||||||
|
"boot_index": 0,
|
||||||
|
"created_at": "2016-08-18T22:28:48.643434+11:11",
|
||||||
|
"extra": {},
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "http://127.0.0.1:6385/v1/volume/targets/<ID>",
|
||||||
|
"rel": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "http://127.0.0.1:6385/volume/targets/<ID>",
|
||||||
|
"rel": "bookmark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"node_uuid": "6d85703a-565d-469a-96ce-30b6de53079d",
|
||||||
|
"properties": {},
|
||||||
|
"updated_at": None,
|
||||||
|
"uuid": "bd4d008c-7d31-463d-abf9-6c23d9d55f7f",
|
||||||
|
"volume_id": "04452bed-5367-4202-8bf5-de4335ac56d2",
|
||||||
|
"volume_type": "iscsi"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestVolumeTarget(base.TestCase):
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
sot = volume_target.VolumeTarget()
|
||||||
|
self.assertIsNone(sot.resource_key)
|
||||||
|
self.assertEqual('targets', sot.resources_key)
|
||||||
|
self.assertEqual('/volume/targets', sot.base_path)
|
||||||
|
self.assertTrue(sot.allow_create)
|
||||||
|
self.assertTrue(sot.allow_fetch)
|
||||||
|
self.assertTrue(sot.allow_commit)
|
||||||
|
self.assertTrue(sot.allow_delete)
|
||||||
|
self.assertTrue(sot.allow_list)
|
||||||
|
self.assertEqual('PATCH', sot.commit_method)
|
||||||
|
|
||||||
|
def test_instantiate(self):
|
||||||
|
sot = volume_target.VolumeTarget(**FAKE)
|
||||||
|
self.assertEqual(FAKE['boot_index'], sot.boot_index)
|
||||||
|
self.assertEqual(FAKE['created_at'], sot.created_at)
|
||||||
|
self.assertEqual(FAKE['extra'], sot.extra)
|
||||||
|
self.assertEqual(FAKE['links'], sot.links)
|
||||||
|
self.assertEqual(FAKE['node_uuid'], sot.node_id)
|
||||||
|
self.assertEqual(FAKE['properties'], sot.properties)
|
||||||
|
self.assertEqual(FAKE['updated_at'], sot.updated_at)
|
||||||
|
self.assertEqual(FAKE['uuid'], sot.id)
|
||||||
|
self.assertEqual(FAKE['volume_id'], sot.volume_id)
|
||||||
|
self.assertEqual(FAKE['volume_type'], sot.volume_type)
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support for Ironic Volume Target API.
|
Loading…
Reference in New Issue
Block a user