Removing baremetal tests from tempest tree

ironic project now has a tempest plugin and its tests will be maintained there.

Closes-Bug: #1614516
Depends-On: I0b7e32dfad2ed63f9dd4d7cad130da39bc869a8a
Change-Id: Id518a6d87d0949737cd1c50cb6a83149b85e5f85
This commit is contained in:
Thiago Paiva 2016-08-15 14:55:58 -03:00 committed by Andrea Frittoli
parent bc80debd90
commit 66cded2553
27 changed files with 23 additions and 2181 deletions

View File

@ -1,25 +0,0 @@
Tempest Field Guide to Baremetal API tests
==========================================
What are these tests?
---------------------
These tests stress the OpenStack baremetal provisioning API provided by
Ironic.
Why are these tests in tempest?
------------------------------
The purpose of these tests is to exercise the various APIs provided by Ironic
for managing baremetal nodes.
Scope of these tests
--------------------
The baremetal API test perform basic CRUD operations on the Ironic node
inventory. They do not actually perform hardware provisioning. It is important
to note that all Ironic API actions are admin operations meant to be used
either by cloud operators or other OpenStack services (i.e., Nova).

View File

@ -1,206 +0,0 @@
# 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.
import functools
from tempest.common.utils import data_utils
from tempest import config
from tempest.lib import exceptions as lib_exc
from tempest import test
CONF = config.CONF
# NOTE(adam_g): The baremetal API tests exercise operations such as enroll
# node, power on, power off, etc. Testing against real drivers (ie, IPMI)
# will require passing driver-specific data to Tempest (addresses,
# credentials, etc). Until then, only support testing against the fake driver,
# which has no external dependencies.
SUPPORTED_DRIVERS = ['fake']
# NOTE(jroll): resources must be deleted in a specific order, this list
# defines the resource types to clean up, and the correct order.
RESOURCE_TYPES = ['port', 'node', 'chassis']
def creates(resource):
"""Decorator that adds resources to the appropriate cleanup list."""
def decorator(f):
@functools.wraps(f)
def wrapper(cls, *args, **kwargs):
resp, body = f(cls, *args, **kwargs)
if 'uuid' in body:
cls.created_objects[resource].add(body['uuid'])
return resp, body
return wrapper
return decorator
class BaseBaremetalTest(test.BaseTestCase):
"""Base class for Baremetal API tests."""
credentials = ['admin']
@classmethod
def skip_checks(cls):
super(BaseBaremetalTest, cls).skip_checks()
if not CONF.service_available.ironic:
skip_msg = ('%s skipped as Ironic is not available' % cls.__name__)
raise cls.skipException(skip_msg)
if CONF.baremetal.driver not in SUPPORTED_DRIVERS:
skip_msg = ('%s skipped as Ironic driver %s is not supported for '
'testing.' %
(cls.__name__, CONF.baremetal.driver))
raise cls.skipException(skip_msg)
@classmethod
def setup_clients(cls):
super(BaseBaremetalTest, cls).setup_clients()
cls.client = cls.os_admin.baremetal_client
@classmethod
def resource_setup(cls):
super(BaseBaremetalTest, cls).resource_setup()
cls.driver = CONF.baremetal.driver
cls.power_timeout = CONF.baremetal.power_timeout
cls.created_objects = {}
for resource in RESOURCE_TYPES:
cls.created_objects[resource] = set()
@classmethod
def resource_cleanup(cls):
"""Ensure that all created objects get destroyed."""
try:
for resource in RESOURCE_TYPES:
uuids = cls.created_objects[resource]
delete_method = getattr(cls.client, 'delete_%s' % resource)
for u in uuids:
delete_method(u, ignore_errors=lib_exc.NotFound)
finally:
super(BaseBaremetalTest, cls).resource_cleanup()
@classmethod
@creates('chassis')
def create_chassis(cls, description=None):
"""Wrapper utility for creating test chassis.
:param description: A description of the chassis. If not supplied,
a random value will be generated.
:return: Created chassis.
"""
description = description or data_utils.rand_name('test-chassis')
resp, body = cls.client.create_chassis(description=description)
return resp, body
@classmethod
@creates('node')
def create_node(cls, chassis_id, cpu_arch='x86', cpus=8, local_gb=10,
memory_mb=4096):
"""Wrapper utility for creating test baremetal nodes.
:param chassis_id: The unique identifier of the chassis.
:param cpu_arch: CPU architecture of the node. Default: x86.
:param cpus: Number of CPUs. Default: 8.
:param local_gb: Disk size. Default: 10.
:param memory_mb: Available RAM. Default: 4096.
:return: Created node.
"""
resp, body = cls.client.create_node(chassis_id, cpu_arch=cpu_arch,
cpus=cpus, local_gb=local_gb,
memory_mb=memory_mb,
driver=cls.driver)
return resp, body
@classmethod
@creates('port')
def create_port(cls, node_id, address, extra=None, uuid=None):
"""Wrapper utility for creating test ports.
:param node_id: The unique identifier of the node.
:param address: MAC address of the port.
:param extra: Meta data of the port. If not supplied, an empty
dictionary will be created.
:param uuid: UUID of the port.
:return: Created port.
"""
extra = extra or {}
resp, body = cls.client.create_port(address=address, node_id=node_id,
extra=extra, uuid=uuid)
return resp, body
@classmethod
def delete_chassis(cls, chassis_id):
"""Deletes a chassis having the specified UUID.
:param chassis_id: The unique identifier of the chassis.
:return: Server response.
"""
resp, body = cls.client.delete_chassis(chassis_id)
if chassis_id in cls.created_objects['chassis']:
cls.created_objects['chassis'].remove(chassis_id)
return resp
@classmethod
def delete_node(cls, node_id):
"""Deletes a node having the specified UUID.
:param node_id: The unique identifier of the node.
:return: Server response.
"""
resp, body = cls.client.delete_node(node_id)
if node_id in cls.created_objects['node']:
cls.created_objects['node'].remove(node_id)
return resp
@classmethod
def delete_port(cls, port_id):
"""Deletes a port having the specified UUID.
:param port_id: The unique identifier of the port.
:return: Server response.
"""
resp, body = cls.client.delete_port(port_id)
if port_id in cls.created_objects['port']:
cls.created_objects['port'].remove(port_id)
return resp
def validate_self_link(self, resource, uuid, link):
"""Check whether the given self link formatted correctly."""
expected_link = "{base}/{pref}/{res}/{uuid}".format(
base=self.client.base_url,
pref=self.client.uri_prefix,
res=resource,
uuid=uuid)
self.assertEqual(expected_link, link)

View File

@ -1,42 +0,0 @@
# 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 tempest.api.baremetal.admin import base
from tempest import test
class TestApiDiscovery(base.BaseBaremetalTest):
"""Tests for API discovery features."""
@test.idempotent_id('a3c27e94-f56c-42c4-8600-d6790650b9c5')
def test_api_versions(self):
_, descr = self.client.get_api_description()
expected_versions = ('v1',)
versions = [version['id'] for version in descr['versions']]
for v in expected_versions:
self.assertIn(v, versions)
@test.idempotent_id('896283a6-488e-4f31-af78-6614286cbe0d')
def test_default_version(self):
_, descr = self.client.get_api_description()
default_version = descr['default_version']
self.assertEqual(default_version['id'], 'v1')
@test.idempotent_id('abc0b34d-e684-4546-9728-ab7a9ad9f174')
def test_version_1_resources(self):
_, descr = self.client.get_version_description(version='v1')
expected_resources = ('nodes', 'chassis',
'ports', 'links', 'media_types')
for res in expected_resources:
self.assertIn(res, descr)

View File

@ -1,85 +0,0 @@
# -*- coding: utf-8 -*-
# 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.
import six
from tempest.api.baremetal.admin import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
class TestChassis(base.BaseBaremetalTest):
"""Tests for chassis."""
@classmethod
def resource_setup(cls):
super(TestChassis, cls).resource_setup()
_, cls.chassis = cls.create_chassis()
def _assertExpected(self, expected, actual):
# Check if not expected keys/values exists in actual response body
for key, value in six.iteritems(expected):
if key not in ('created_at', 'updated_at'):
self.assertIn(key, actual)
self.assertEqual(value, actual[key])
@test.idempotent_id('7c5a2e09-699c-44be-89ed-2bc189992d42')
def test_create_chassis(self):
descr = data_utils.rand_name('test-chassis')
_, chassis = self.create_chassis(description=descr)
self.assertEqual(chassis['description'], descr)
@test.idempotent_id('cabe9c6f-dc16-41a7-b6b9-0a90c212edd5')
def test_create_chassis_unicode_description(self):
# Use a unicode string for testing:
# 'We ♡ OpenStack in Ukraine'
descr = u'В Україні ♡ OpenStack!'
_, chassis = self.create_chassis(description=descr)
self.assertEqual(chassis['description'], descr)
@test.idempotent_id('c84644df-31c4-49db-a307-8942881f41c0')
def test_show_chassis(self):
_, chassis = self.client.show_chassis(self.chassis['uuid'])
self._assertExpected(self.chassis, chassis)
@test.idempotent_id('29c9cd3f-19b5-417b-9864-99512c3b33b3')
def test_list_chassis(self):
_, body = self.client.list_chassis()
self.assertIn(self.chassis['uuid'],
[i['uuid'] for i in body['chassis']])
@test.idempotent_id('5ae649ad-22d1-4fe1-bbc6-97227d199fb3')
def test_delete_chassis(self):
_, body = self.create_chassis()
uuid = body['uuid']
self.delete_chassis(uuid)
self.assertRaises(lib_exc.NotFound, self.client.show_chassis, uuid)
@test.idempotent_id('cda8a41f-6be2-4cbf-840c-994b00a89b44')
def test_update_chassis(self):
_, body = self.create_chassis()
uuid = body['uuid']
new_description = data_utils.rand_name('new-description')
_, body = (self.client.update_chassis(uuid,
description=new_description))
_, chassis = self.client.show_chassis(uuid)
self.assertEqual(chassis['description'], new_description)
@test.idempotent_id('76305e22-a4e2-4ab3-855c-f4e2368b9335')
def test_chassis_node_list(self):
_, node = self.create_node(self.chassis['uuid'])
_, body = self.client.list_chassis_nodes(self.chassis['uuid'])
self.assertIn(node['uuid'], [n['uuid'] for n in body['nodes']])

View File

@ -1,38 +0,0 @@
# Copyright 2014 NEC Corporation. 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 tempest.api.baremetal.admin import base
from tempest import config
from tempest import test
CONF = config.CONF
class TestDrivers(base.BaseBaremetalTest):
"""Tests for drivers."""
@classmethod
def resource_setup(cls):
super(TestDrivers, cls).resource_setup()
cls.driver_name = CONF.baremetal.driver
@test.idempotent_id('5aed2790-7592-4655-9b16-99abcc2e6ec5')
def test_list_drivers(self):
_, drivers = self.client.list_drivers()
self.assertIn(self.driver_name,
[d['name'] for d in drivers['drivers']])
@test.idempotent_id('fb3287a3-c4d7-44bf-ae9d-1eef906d78ce')
def test_show_driver(self):
_, driver = self.client.show_driver(self.driver_name)
self.assertEqual(self.driver_name, driver['name'])

View File

@ -1,169 +0,0 @@
# 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.
import six
from tempest.api.baremetal.admin import base
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest.lib import exceptions as lib_exc
from tempest import test
class TestNodes(base.BaseBaremetalTest):
"""Tests for baremetal nodes."""
def setUp(self):
super(TestNodes, self).setUp()
_, self.chassis = self.create_chassis()
_, self.node = self.create_node(self.chassis['uuid'])
def _assertExpected(self, expected, actual):
# Check if not expected keys/values exists in actual response body
for key, value in six.iteritems(expected):
if key not in ('created_at', 'updated_at'):
self.assertIn(key, actual)
self.assertEqual(value, actual[key])
def _associate_node_with_instance(self):
self.client.set_node_power_state(self.node['uuid'], 'power off')
waiters.wait_for_bm_node_status(self.client, self.node['uuid'],
'power_state', 'power off')
instance_uuid = data_utils.rand_uuid()
self.client.update_node(self.node['uuid'],
instance_uuid=instance_uuid)
self.addCleanup(self.client.update_node,
uuid=self.node['uuid'], instance_uuid=None)
return instance_uuid
@test.idempotent_id('4e939eb2-8a69-4e84-8652-6fffcbc9db8f')
def test_create_node(self):
params = {'cpu_arch': 'x86_64',
'cpus': '12',
'local_gb': '10',
'memory_mb': '1024'}
_, body = self.create_node(self.chassis['uuid'], **params)
self._assertExpected(params, body['properties'])
@test.idempotent_id('9ade60a4-505e-4259-9ec4-71352cbbaf47')
def test_delete_node(self):
_, node = self.create_node(self.chassis['uuid'])
self.delete_node(node['uuid'])
self.assertRaises(lib_exc.NotFound, self.client.show_node,
node['uuid'])
@test.idempotent_id('55451300-057c-4ecf-8255-ba42a83d3a03')
def test_show_node(self):
_, loaded_node = self.client.show_node(self.node['uuid'])
self._assertExpected(self.node, loaded_node)
@test.idempotent_id('4ca123c4-160d-4d8d-a3f7-15feda812263')
def test_list_nodes(self):
_, body = self.client.list_nodes()
self.assertIn(self.node['uuid'],
[i['uuid'] for i in body['nodes']])
@test.idempotent_id('85b1f6e0-57fd-424c-aeff-c3422920556f')
def test_list_nodes_association(self):
_, body = self.client.list_nodes(associated=True)
self.assertNotIn(self.node['uuid'],
[n['uuid'] for n in body['nodes']])
self._associate_node_with_instance()
_, body = self.client.list_nodes(associated=True)
self.assertIn(self.node['uuid'], [n['uuid'] for n in body['nodes']])
_, body = self.client.list_nodes(associated=False)
self.assertNotIn(self.node['uuid'], [n['uuid'] for n in body['nodes']])
@test.idempotent_id('18c4ebd8-f83a-4df7-9653-9fb33a329730')
def test_node_port_list(self):
_, port = self.create_port(self.node['uuid'],
data_utils.rand_mac_address())
_, body = self.client.list_node_ports(self.node['uuid'])
self.assertIn(port['uuid'],
[p['uuid'] for p in body['ports']])
@test.idempotent_id('72591acb-f215-49db-8395-710d14eb86ab')
def test_node_port_list_no_ports(self):
_, node = self.create_node(self.chassis['uuid'])
_, body = self.client.list_node_ports(node['uuid'])
self.assertEmpty(body['ports'])
@test.idempotent_id('4fed270a-677a-4d19-be87-fd38ae490320')
def test_update_node(self):
props = {'cpu_arch': 'x86_64',
'cpus': '12',
'local_gb': '10',
'memory_mb': '128'}
_, node = self.create_node(self.chassis['uuid'], **props)
new_p = {'cpu_arch': 'x86',
'cpus': '1',
'local_gb': '10000',
'memory_mb': '12300'}
_, body = self.client.update_node(node['uuid'], properties=new_p)
_, node = self.client.show_node(node['uuid'])
self._assertExpected(new_p, node['properties'])
@test.idempotent_id('cbf1f515-5f4b-4e49-945c-86bcaccfeb1d')
def test_validate_driver_interface(self):
_, body = self.client.validate_driver_interface(self.node['uuid'])
core_interfaces = ['power', 'deploy']
for interface in core_interfaces:
self.assertIn(interface, body)
@test.idempotent_id('5519371c-26a2-46e9-aa1a-f74226e9d71f')
def test_set_node_boot_device(self):
self.client.set_node_boot_device(self.node['uuid'], 'pxe')
@test.idempotent_id('9ea73775-f578-40b9-bc34-efc639c4f21f')
def test_get_node_boot_device(self):
body = self.client.get_node_boot_device(self.node['uuid'])
self.assertIn('boot_device', body)
self.assertIn('persistent', body)
self.assertIsInstance(body['boot_device'], six.string_types)
self.assertIsInstance(body['persistent'], bool)
@test.idempotent_id('3622bc6f-3589-4bc2-89f3-50419c66b133')
def test_get_node_supported_boot_devices(self):
body = self.client.get_node_supported_boot_devices(self.node['uuid'])
self.assertIn('supported_boot_devices', body)
self.assertIsInstance(body['supported_boot_devices'], list)
@test.idempotent_id('f63b6288-1137-4426-8cfe-0d5b7eb87c06')
def test_get_console(self):
_, body = self.client.get_console(self.node['uuid'])
con_info = ['console_enabled', 'console_info']
for key in con_info:
self.assertIn(key, body)
@test.idempotent_id('80504575-9b21-4670-92d1-143b948f9437')
def test_set_console_mode(self):
self.client.set_console_mode(self.node['uuid'], True)
_, body = self.client.get_console(self.node['uuid'])
self.assertEqual(True, body['console_enabled'])
@test.idempotent_id('b02a4f38-5e8b-44b2-aed2-a69a36ecfd69')
def test_get_node_by_instance_uuid(self):
instance_uuid = self._associate_node_with_instance()
_, body = self.client.show_node_by_instance_uuid(instance_uuid)
self.assertEqual(len(body['nodes']), 1)
self.assertIn(self.node['uuid'], [n['uuid'] for n in body['nodes']])

View File

@ -1,59 +0,0 @@
# Copyright 2014 NEC Corporation. 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 oslo_utils import timeutils
from tempest.api.baremetal.admin import base
from tempest.lib import exceptions
from tempest import test
class TestNodeStates(base.BaseBaremetalTest):
"""Tests for baremetal NodeStates."""
@classmethod
def resource_setup(cls):
super(TestNodeStates, cls).resource_setup()
_, cls.chassis = cls.create_chassis()
_, cls.node = cls.create_node(cls.chassis['uuid'])
def _validate_power_state(self, node_uuid, power_state):
# Validate that power state is set within timeout
if power_state == 'rebooting':
power_state = 'power on'
start = timeutils.utcnow()
while timeutils.delta_seconds(
start, timeutils.utcnow()) < self.power_timeout:
_, node = self.client.show_node(node_uuid)
if node['power_state'] == power_state:
return
message = ('Failed to set power state within '
'the required time: %s sec.' % self.power_timeout)
raise exceptions.TimeoutException(message)
@test.idempotent_id('cd8afa5e-3f57-4e43-8185-beb83d3c9015')
def test_list_nodestates(self):
_, nodestates = self.client.list_nodestates(self.node['uuid'])
for key in nodestates:
self.assertEqual(nodestates[key], self.node[key])
@test.idempotent_id('fc5b9320-0c98-4e5a-8848-877fe5a0322c')
def test_set_node_power_state(self):
_, node = self.create_node(self.chassis['uuid'])
states = ["power on", "rebooting", "power off"]
for state in states:
# Set power state
self.client.set_node_power_state(node['uuid'], state)
# Check power state after state is set
self._validate_power_state(node['uuid'], state)

View File

@ -1,266 +0,0 @@
# 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.
import six
from tempest.api.baremetal.admin import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
class TestPorts(base.BaseBaremetalTest):
"""Tests for ports."""
def setUp(self):
super(TestPorts, self).setUp()
_, self.chassis = self.create_chassis()
_, self.node = self.create_node(self.chassis['uuid'])
_, self.port = self.create_port(self.node['uuid'],
data_utils.rand_mac_address())
def _assertExpected(self, expected, actual):
# Check if not expected keys/values exists in actual response body
for key, value in six.iteritems(expected):
if key not in ('created_at', 'updated_at'):
self.assertIn(key, actual)
self.assertEqual(value, actual[key])
@test.idempotent_id('83975898-2e50-42ed-b5f0-e510e36a0b56')
def test_create_port(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
_, body = self.client.show_port(port['uuid'])
self._assertExpected(port, body)
@test.idempotent_id('d1f6b249-4cf6-4fe6-9ed6-a6e84b1bf67b')
def test_create_port_specifying_uuid(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
uuid = data_utils.rand_uuid()
_, port = self.create_port(node_id=node_id,
address=address, uuid=uuid)
_, body = self.client.show_port(uuid)
self._assertExpected(port, body)
@test.idempotent_id('4a02c4b0-6573-42a4-a513-2e36ad485b62')
def test_create_port_with_extra(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
extra = {'str': 'value', 'int': 123, 'float': 0.123,
'bool': True, 'list': [1, 2, 3], 'dict': {'foo': 'bar'}}
_, port = self.create_port(node_id=node_id, address=address,
extra=extra)
_, body = self.client.show_port(port['uuid'])
self._assertExpected(port, body)
@test.idempotent_id('1bf257a9-aea3-494e-89c0-63f657ab4fdd')
def test_delete_port(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
self.delete_port(port['uuid'])
self.assertRaises(lib_exc.NotFound, self.client.show_port,
port['uuid'])
@test.idempotent_id('9fa77ab5-ce59-4f05-baac-148904ba1597')
def test_show_port(self):
_, port = self.client.show_port(self.port['uuid'])
self._assertExpected(self.port, port)
@test.idempotent_id('7c1114ff-fc3f-47bb-bc2f-68f61620ba8b')
def test_show_port_by_address(self):
_, port = self.client.show_port_by_address(self.port['address'])
self._assertExpected(self.port, port['ports'][0])
@test.idempotent_id('bd773405-aea5-465d-b576-0ab1780069e5')
def test_show_port_with_links(self):
_, port = self.client.show_port(self.port['uuid'])
self.assertIn('links', port.keys())
self.assertEqual(2, len(port['links']))
self.assertIn(port['uuid'], port['links'][0]['href'])
@test.idempotent_id('b5e91854-5cd7-4a8e-bb35-3e0a1314606d')
def test_list_ports(self):
_, body = self.client.list_ports()
self.assertIn(self.port['uuid'],
[i['uuid'] for i in body['ports']])
# Verify self links.
for port in body['ports']:
self.validate_self_link('ports', port['uuid'],
port['links'][0]['href'])
@test.idempotent_id('324a910e-2f80-4258-9087-062b5ae06240')
def test_list_with_limit(self):
_, body = self.client.list_ports(limit=3)
next_marker = body['ports'][-1]['uuid']
self.assertIn(next_marker, body['next'])
@test.idempotent_id('8a94b50f-9895-4a63-a574-7ecff86e5875')
def test_list_ports_details(self):
node_id = self.node['uuid']
uuids = [
self.create_port(node_id=node_id,
address=data_utils.rand_mac_address())
[1]['uuid'] for i in range(0, 5)]
_, body = self.client.list_ports_detail()
ports_dict = dict((port['uuid'], port) for port in body['ports']
if port['uuid'] in uuids)
for uuid in uuids:
self.assertIn(uuid, ports_dict)
port = ports_dict[uuid]
self.assertIn('extra', port)
self.assertIn('node_uuid', port)
# never expose the node_id
self.assertNotIn('node_id', port)
# Verify self link.
self.validate_self_link('ports', port['uuid'],
port['links'][0]['href'])
@test.idempotent_id('8a03f688-7d75-4ecd-8cbc-e06b8f346738')
def test_list_ports_details_with_address(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
self.create_port(node_id=node_id, address=address)
for i in range(0, 5):
self.create_port(node_id=node_id,
address=data_utils.rand_mac_address())
_, body = self.client.list_ports_detail(address=address)
self.assertEqual(1, len(body['ports']))
self.assertEqual(address, body['ports'][0]['address'])
@test.idempotent_id('9c26298b-1bcb-47b7-9b9e-8bdd6e3c4aba')
def test_update_port_replace(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
extra = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
_, port = self.create_port(node_id=node_id, address=address,
extra=extra)
new_address = data_utils.rand_mac_address()
new_extra = {'key1': 'new-value1', 'key2': 'new-value2',
'key3': 'new-value3'}
patch = [{'path': '/address',
'op': 'replace',
'value': new_address},
{'path': '/extra/key1',
'op': 'replace',
'value': new_extra['key1']},
{'path': '/extra/key2',
'op': 'replace',
'value': new_extra['key2']},
{'path': '/extra/key3',
'op': 'replace',
'value': new_extra['key3']}]
self.client.update_port(port['uuid'], patch)
_, body = self.client.show_port(port['uuid'])
self.assertEqual(new_address, body['address'])
self.assertEqual(new_extra, body['extra'])
@test.idempotent_id('d7e7fece-6ed9-460a-9ebe-9267217e8580')
def test_update_port_remove(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
extra = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
_, port = self.create_port(node_id=node_id, address=address,
extra=extra)
# Removing one item from the collection
self.client.update_port(port['uuid'],
[{'path': '/extra/key2',
'op': 'remove'}])
extra.pop('key2')
_, body = self.client.show_port(port['uuid'])
self.assertEqual(extra, body['extra'])
# Removing the collection
self.client.update_port(port['uuid'], [{'path': '/extra',
'op': 'remove'}])
_, body = self.client.show_port(port['uuid'])
self.assertEqual({}, body['extra'])
# Assert nothing else was changed
self.assertEqual(node_id, body['node_uuid'])
self.assertEqual(address, body['address'])
@test.idempotent_id('241288b3-e98a-400f-a4d7-d1f716146361')
def test_update_port_add(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
extra = {'key1': 'value1', 'key2': 'value2'}
patch = [{'path': '/extra/key1',
'op': 'add',
'value': extra['key1']},
{'path': '/extra/key2',
'op': 'add',
'value': extra['key2']}]
self.client.update_port(port['uuid'], patch)
_, body = self.client.show_port(port['uuid'])
self.assertEqual(extra, body['extra'])
@test.idempotent_id('5309e897-0799-4649-a982-0179b04c3876')
def test_update_port_mixed_ops(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
extra = {'key1': 'value1', 'key2': 'value2'}
_, port = self.create_port(node_id=node_id, address=address,
extra=extra)
new_address = data_utils.rand_mac_address()
new_extra = {'key1': 0.123, 'key3': {'cat': 'meow'}}
patch = [{'path': '/address',
'op': 'replace',
'value': new_address},
{'path': '/extra/key1',
'op': 'replace',
'value': new_extra['key1']},
{'path': '/extra/key2',
'op': 'remove'},
{'path': '/extra/key3',
'op': 'add',
'value': new_extra['key3']}]
self.client.update_port(port['uuid'], patch)
_, body = self.client.show_port(port['uuid'])
self.assertEqual(new_address, body['address'])
self.assertEqual(new_extra, body['extra'])

View File

@ -1,338 +0,0 @@
# 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 tempest.api.baremetal.admin import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
class TestPortsNegative(base.BaseBaremetalTest):
"""Negative tests for ports."""
def setUp(self):
super(TestPortsNegative, self).setUp()
_, self.chassis = self.create_chassis()
_, self.node = self.create_node(self.chassis['uuid'])
@test.attr(type=['negative'])
@test.idempotent_id('0a6ee1f7-d0d9-4069-8778-37f3aa07303a')
def test_create_port_malformed_mac(self):
node_id = self.node['uuid']
address = 'malformed:mac'
self.assertRaises(lib_exc.BadRequest,
self.create_port, node_id=node_id, address=address)
@test.attr(type=['negative'])
@test.idempotent_id('30277ee8-0c60-4f1d-b125-0e51c2f43369')
def test_create_port_nonexsistent_node_id(self):
node_id = data_utils.rand_uuid()
address = data_utils.rand_mac_address()
self.assertRaises(lib_exc.BadRequest, self.create_port,
node_id=node_id, address=address)
@test.attr(type=['negative'])
@test.idempotent_id('029190f6-43e1-40a3-b64a-65173ba653a3')
def test_show_port_malformed_uuid(self):
self.assertRaises(lib_exc.BadRequest, self.client.show_port,
'malformed:uuid')
@test.attr(type=['negative'])
@test.idempotent_id('0d00e13d-e2e0-45b1-bcbc-55a6d90ca793')
def test_show_port_nonexistent_uuid(self):
self.assertRaises(lib_exc.NotFound, self.client.show_port,
data_utils.rand_uuid())
@test.attr(type=['negative'])
@test.idempotent_id('4ad85266-31e9-4942-99ac-751897dc9e23')
def test_show_port_by_mac_not_allowed(self):
self.assertRaises(lib_exc.BadRequest, self.client.show_port,
data_utils.rand_mac_address())
@test.attr(type=['negative'])
@test.idempotent_id('89a34380-3c61-4c32-955c-2cd9ce94da21')
def test_create_port_duplicated_port_uuid(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
uuid = data_utils.rand_uuid()
self.create_port(node_id=node_id, address=address, uuid=uuid)
self.assertRaises(lib_exc.Conflict, self.create_port, node_id=node_id,
address=address, uuid=uuid)
@test.attr(type=['negative'])
@test.idempotent_id('65e84917-733c-40ae-ae4b-96a4adff931c')
def test_create_port_no_mandatory_field_node_id(self):
address = data_utils.rand_mac_address()
self.assertRaises(lib_exc.BadRequest, self.create_port, node_id=None,
address=address)
@test.attr(type=['negative'])
@test.idempotent_id('bcea3476-7033-4183-acfe-e56a30809b46')
def test_create_port_no_mandatory_field_mac(self):
node_id = self.node['uuid']
self.assertRaises(lib_exc.BadRequest, self.create_port,
node_id=node_id, address=None)
@test.attr(type=['negative'])
@test.idempotent_id('2b51cd18-fb95-458b-9780-e6257787b649')
def test_create_port_malformed_port_uuid(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
uuid = 'malformed:uuid'
self.assertRaises(lib_exc.BadRequest, self.create_port,
node_id=node_id, address=address, uuid=uuid)
@test.attr(type=['negative'])
@test.idempotent_id('583a6856-6a30-4ac4-889f-14e2adff8105')
def test_create_port_malformed_node_id(self):
address = data_utils.rand_mac_address()
self.assertRaises(lib_exc.BadRequest, self.create_port,
node_id='malformed:nodeid', address=address)
@test.attr(type=['negative'])
@test.idempotent_id('e27f8b2e-42c6-4a43-a3cd-accff716bc5c')
def test_create_port_duplicated_mac(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
self.create_port(node_id=node_id, address=address)
self.assertRaises(lib_exc.Conflict,
self.create_port, node_id=node_id,
address=address)
@test.attr(type=['negative'])
@test.idempotent_id('8907082d-ac5e-4be3-b05f-d072ede82020')
def test_update_port_by_mac_not_allowed(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
extra = {'key': 'value'}
self.create_port(node_id=node_id, address=address, extra=extra)
patch = [{'path': '/extra/key',
'op': 'replace',
'value': 'new-value'}]
self.assertRaises(lib_exc.BadRequest,
self.client.update_port, address,
patch)
@test.attr(type=['negative'])
@test.idempotent_id('df1ac70c-db9f-41d9-90f1-78cd6b905718')
def test_update_port_nonexistent(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
extra = {'key': 'value'}
_, port = self.create_port(node_id=node_id, address=address,
extra=extra)
port_id = port['uuid']
_, body = self.client.delete_port(port_id)
patch = [{'path': '/extra/key',
'op': 'replace',
'value': 'new-value'}]
self.assertRaises(lib_exc.NotFound,
self.client.update_port, port_id, patch)
@test.attr(type=['negative'])
@test.idempotent_id('c701e315-aa52-41ea-817c-65c5ca8ca2a8')
def test_update_port_malformed_port_uuid(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
self.create_port(node_id=node_id, address=address)
new_address = data_utils.rand_mac_address()
self.assertRaises(lib_exc.BadRequest, self.client.update_port,
uuid='malformed:uuid',
patch=[{'path': '/address', 'op': 'replace',
'value': new_address}])
@test.attr(type=['negative'])
@test.idempotent_id('f8f15803-34d6-45dc-b06f-e5e04bf1b38b')
def test_update_port_add_nonexistent_property(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
port_id = port['uuid']
self.assertRaises(lib_exc.BadRequest, self.client.update_port, port_id,
[{'path': '/nonexistent', ' op': 'add',
'value': 'value'}])
@test.attr(type=['negative'])
@test.idempotent_id('898ec904-38b1-4fcb-9584-1187d4263a2a')
def test_update_port_replace_node_id_with_malformed(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
port_id = port['uuid']
patch = [{'path': '/node_uuid',
'op': 'replace',
'value': 'malformed:node_uuid'}]
self.assertRaises(lib_exc.BadRequest,
self.client.update_port, port_id, patch)
@test.attr(type=['negative'])
@test.idempotent_id('2949f30f-5f59-43fa-a6d9-4eac578afab4')
def test_update_port_replace_mac_with_duplicated(self):
node_id = self.node['uuid']
address1 = data_utils.rand_mac_address()
address2 = data_utils.rand_mac_address()
_, port1 = self.create_port(node_id=node_id, address=address1)
_, port2 = self.create_port(node_id=node_id, address=address2)
port_id = port2['uuid']
patch = [{'path': '/address',
'op': 'replace',
'value': address1}]
self.assertRaises(lib_exc.Conflict,
self.client.update_port, port_id, patch)
@test.attr(type=['negative'])
@test.idempotent_id('97f6e048-6e4f-4eba-a09d-fbbc78b77a77')
def test_update_port_replace_node_id_with_nonexistent(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
port_id = port['uuid']
patch = [{'path': '/node_uuid',
'op': 'replace',
'value': data_utils.rand_uuid()}]
self.assertRaises(lib_exc.BadRequest,
self.client.update_port, port_id, patch)
@test.attr(type=['negative'])
@test.idempotent_id('375022c5-9e9e-4b11-9ca4-656729c0c9b2')
def test_update_port_replace_mac_with_malformed(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
port_id = port['uuid']
patch = [{'path': '/address',
'op': 'replace',
'value': 'malformed:mac'}]
self.assertRaises(lib_exc.BadRequest,
self.client.update_port, port_id, patch)
@test.attr(type=['negative'])
@test.idempotent_id('5722b853-03fc-4854-8308-2036a1b67d85')
def test_update_port_replace_nonexistent_property(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
port_id = port['uuid']
patch = [{'path': '/nonexistent', ' op': 'replace', 'value': 'value'}]
self.assertRaises(lib_exc.BadRequest,
self.client.update_port, port_id, patch)
@test.attr(type=['negative'])
@test.idempotent_id('ae2696ca-930a-4a7f-918f-30ae97c60f56')
def test_update_port_remove_mandatory_field_mac(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
port_id = port['uuid']
self.assertRaises(lib_exc.BadRequest, self.client.update_port, port_id,
[{'path': '/address', 'op': 'remove'}])
@test.attr(type=['negative'])
@test.idempotent_id('5392c1f0-2071-4697-9064-ec2d63019018')
def test_update_port_remove_mandatory_field_port_uuid(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
port_id = port['uuid']
self.assertRaises(lib_exc.BadRequest, self.client.update_port, port_id,
[{'path': '/uuid', 'op': 'remove'}])
@test.attr(type=['negative'])
@test.idempotent_id('06b50d82-802a-47ef-b079-0a3311cf85a2')
def test_update_port_remove_nonexistent_property(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
_, port = self.create_port(node_id=node_id, address=address)
port_id = port['uuid']
self.assertRaises(lib_exc.BadRequest, self.client.update_port, port_id,
[{'path': '/nonexistent', 'op': 'remove'}])
@test.attr(type=['negative'])
@test.idempotent_id('03d42391-2145-4a6c-95bf-63fe55eb64fd')
def test_delete_port_by_mac_not_allowed(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
self.create_port(node_id=node_id, address=address)
self.assertRaises(lib_exc.BadRequest, self.client.delete_port, address)
@test.attr(type=['negative'])
@test.idempotent_id('0629e002-818e-4763-b25b-ae5e07b1cb23')
def test_update_port_mixed_ops_integrity(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
extra = {'key1': 'value1', 'key2': 'value2'}
_, port = self.create_port(node_id=node_id, address=address,
extra=extra)
port_id = port['uuid']
new_address = data_utils.rand_mac_address()
new_extra = {'key1': 'new-value1', 'key3': 'new-value3'}
patch = [{'path': '/address',
'op': 'replace',
'value': new_address},
{'path': '/extra/key1',
'op': 'replace',
'value': new_extra['key1']},
{'path': '/extra/key2',
'op': 'remove'},
{'path': '/extra/key3',
'op': 'add',
'value': new_extra['key3']},
{'path': '/nonexistent',
'op': 'replace',
'value': 'value'}]
self.assertRaises(lib_exc.BadRequest, self.client.update_port, port_id,
patch)
# patch should not be applied
_, body = self.client.show_port(port_id)
self.assertEqual(address, body['address'])
self.assertEqual(extra, body['extra'])

View File

@ -25,7 +25,7 @@ class BaremetalNodesAdminTestJSON(base.BaseV2ComputeAdminTest):
@classmethod
def resource_setup(cls):
super(BaremetalNodesAdminTestJSON, cls).resource_setup()
if not CONF.service_available.ironic:
if not getattr(CONF.service_available, 'ironic', False):
skip_msg = ('%s skipped as Ironic is not available' % cls.__name__)
raise cls.skipException(skip_msg)
cls.client = cls.os_adm.baremetal_nodes_client

View File

@ -21,7 +21,6 @@ from tempest import config
from tempest.lib import auth
from tempest.lib import exceptions as lib_exc
from tempest.lib.services import clients
from tempest.services import baremetal
from tempest.services import identity
from tempest.services import object_storage
from tempest.services import orchestration
@ -35,14 +34,6 @@ class Manager(clients.ServiceClients):
default_params = config.service_client_config()
# TODO(andreaf) This is only used by baremetal clients,
# and should be removed once they are out of Tempest
default_params_with_timeout_values = {
'build_interval': CONF.compute.build_interval,
'build_timeout': CONF.compute.build_timeout
}
default_params_with_timeout_values.update(default_params)
def __init__(self, credentials, service=None, scope='project'):
"""Initialization of Manager class.
@ -66,12 +57,6 @@ class Manager(clients.ServiceClients):
self._set_image_clients()
self._set_network_clients()
self.baremetal_client = baremetal.BaremetalClient(
self.auth_provider,
CONF.baremetal.catalog_type,
CONF.identity.region,
endpoint_type=CONF.baremetal.endpoint_type,
**self.default_params_with_timeout_values)
self.orchestration_client = orchestration.OrchestrationClient(
self.auth_provider,
CONF.orchestration.catalog_type,
@ -177,7 +162,6 @@ class Manager(clients.ServiceClients):
self.instance_usages_audit_log_client = (
self.compute.InstanceUsagesAuditLogClient())
self.tenant_networks_client = self.compute.TenantNetworksClient()
self.baremetal_nodes_client = self.compute.BaremetalNodesClient()
# NOTE: The following client needs special timeout values because
# the API is a proxy for the other component.

View File

@ -88,7 +88,7 @@ def get_credentials_provider(name, network_resources=None,
project_network_mask_bits=CONF.network.project_network_mask_bits,
public_network_id=CONF.network.public_network_id,
create_networks=(CONF.auth.create_isolated_networks and not
CONF.baremetal.driver_enabled),
CONF.network.shared_physical_network),
resource_prefix=CONF.resources_prefix,
**get_dynamic_provider_params())
else:

View File

@ -231,35 +231,6 @@ def wait_for_backup_status(client, backup_id, status):
raise lib_exc.TimeoutException(message)
def wait_for_bm_node_status(client, node_id, attr, status):
"""Waits for a baremetal node attribute to reach given status.
The client should have a show_node(node_uuid) method to get the node.
"""
_, node = client.show_node(node_id)
start = int(time.time())
while node[attr] != status:
time.sleep(client.build_interval)
_, node = client.show_node(node_id)
status_curr = node[attr]
if status_curr == status:
return
if int(time.time()) - start >= client.build_timeout:
message = ('Node %(node_id)s failed to reach %(attr)s=%(status)s '
'within the required time (%(timeout)s s).' %
{'node_id': node_id,
'attr': attr,
'status': status,
'timeout': client.build_timeout})
message += ' Current state of %s: %s.' % (attr, status_curr)
caller = test_utils.find_test_caller()
if caller:
message = '(%s) %s' % (caller, message)
raise lib_exc.TimeoutException(message)
def wait_for_qos_operations(client, qos_id, operation, args=None):
"""Waits for a qos operations to be completed.

View File

@ -565,6 +565,10 @@ NetworkGroup = [
default=["1.0.0.0/16", "2.0.0.0/16"],
help="List of ip pools"
" for subnetpools creation"),
cfg.BoolOpt('shared_physical_network',
default=False,
help="The environment does not support network separation "
"between tenants."),
# TODO(ylobankov): Delete this option once the Liberty release is EOL.
cfg.BoolOpt('dvr_extra_resources',
default=True,
@ -969,9 +973,6 @@ ServiceAvailableGroup = [
cfg.BoolOpt('sahara',
default=False,
help="Whether or not Sahara is expected to be available"),
cfg.BoolOpt('ironic',
default=False,
help="Whether or not Ironic is expected to be available"),
]
debug_group = cfg.OptGroup(name="debug",
@ -1026,56 +1027,6 @@ InputScenarioGroup = [
deprecated_for_removal=True),
]
baremetal_group = cfg.OptGroup(name='baremetal',
title='Baremetal provisioning service options',
help='When enabling baremetal tests, Nova '
'must be configured to use the Ironic '
'driver. The following parameters for the '
'[compute] section must be disabled: '
'console_output, interface_attach, '
'live_migration, pause, rescue, resize '
'shelve, snapshot, and suspend')
# NOTE(deva): Ironic tests have been ported to tempest.lib. New config options
# should be added to ironic/ironic_tempest_plugin/config.py.
# However, these options need to remain here for testing stable
# branches until Liberty release reaches EOL.
BaremetalGroup = [
cfg.StrOpt('catalog_type',
default='baremetal',
help="Catalog type of the baremetal provisioning service"),
cfg.BoolOpt('driver_enabled',
default=False,
help="Whether the Ironic nova-compute driver is enabled"),
cfg.StrOpt('driver',
default='fake',
help="Driver name which Ironic uses"),
cfg.StrOpt('endpoint_type',
default='publicURL',
choices=['public', 'admin', 'internal',
'publicURL', 'adminURL', 'internalURL'],
help="The endpoint type to use for the baremetal provisioning "
"service"),
cfg.IntOpt('active_timeout',
default=300,
help="Timeout for Ironic node to completely provision"),
cfg.IntOpt('association_timeout',
default=30,
help="Timeout for association of Nova instance and Ironic "
"node"),
cfg.IntOpt('power_timeout',
default=60,
help="Timeout for Ironic power transitions."),
cfg.IntOpt('unprovision_timeout',
default=300,
help="Timeout for unprovisioning an Ironic node. "
"Takes longer since Kilo as Ironic performs an extra "
"step in Node cleaning.")
]
DefaultGroup = [
cfg.StrOpt('resources_prefix',
default='tempest',
@ -1105,7 +1056,6 @@ _opts = [
(scenario_group, ScenarioGroup),
(service_available_group, ServiceAvailableGroup),
(debug_group, DebugGroup),
(baremetal_group, BaremetalGroup),
(input_scenario_group, InputScenarioGroup),
(None, DefaultGroup)
]
@ -1168,7 +1118,6 @@ class TempestConfigPrivate(object):
self.scenario = _CONF.scenario
self.service_available = _CONF.service_available
self.debug = _CONF.debug
self.baremetal = _CONF.baremetal
self.input_scenario = _CONF['input-scenario']
logging.tempest_set_log_file('tempest.log')

View File

@ -157,7 +157,7 @@ class ScenarioTest(tempest.test.BaseTestCase):
# Convert security group names to security group ids
# to pass to create_port
if 'security_groups' in kwargs:
security_groups =\
security_groups = \
clients.security_groups_client.list_security_groups(
).get('security_groups')
sec_dict = dict([(s['name'], s['id'])
@ -817,7 +817,7 @@ class NetworkScenarioTest(ScenarioTest):
# servers. Neutron does not bind ports for Ironic instances, as a
# result the port remains in the DOWN state.
# TODO(vsaienko) remove once bug: #1599836 is resolved.
if CONF.service_available.ironic:
if getattr(CONF.service_available, 'ironic', False):
p_status.append('DOWN')
port_map = [(p["id"], fxip["ip_address"])
for p in ports
@ -1189,7 +1189,7 @@ class NetworkScenarioTest(ScenarioTest):
:param dns_nameservers: list of dns servers to send to subnet.
:returns: network, subnet, router
"""
if CONF.baremetal.driver_enabled:
if CONF.network.shared_physical_network:
# NOTE(Shrews): This exception is for environments where tenant
# credential isolation is available, but network separation is
# not (the current baremetal case). Likely can be removed when
@ -1230,151 +1230,6 @@ class NetworkScenarioTest(ScenarioTest):
return network, subnet, router
# power/provision states as of icehouse
class BaremetalPowerStates(object):
"""Possible power states of an Ironic node."""
POWER_ON = 'power on'
POWER_OFF = 'power off'
REBOOT = 'rebooting'
SUSPEND = 'suspended'
class BaremetalProvisionStates(object):
"""Possible provision states of an Ironic node."""
NOSTATE = None
INIT = 'initializing'
ACTIVE = 'active'
BUILDING = 'building'
DEPLOYWAIT = 'wait call-back'
DEPLOYING = 'deploying'
DEPLOYFAIL = 'deploy failed'
DEPLOYDONE = 'deploy complete'
DELETING = 'deleting'
DELETED = 'deleted'
ERROR = 'error'
class BaremetalScenarioTest(ScenarioTest):
credentials = ['primary', 'admin']
@classmethod
def skip_checks(cls):
super(BaremetalScenarioTest, cls).skip_checks()
if (not CONF.service_available.ironic or
not CONF.baremetal.driver_enabled):
msg = 'Ironic not available or Ironic compute driver not enabled'
raise cls.skipException(msg)
@classmethod
def setup_clients(cls):
super(BaremetalScenarioTest, cls).setup_clients()
cls.baremetal_client = cls.admin_manager.baremetal_client
@classmethod
def resource_setup(cls):
super(BaremetalScenarioTest, cls).resource_setup()
# allow any issues obtaining the node list to raise early
cls.baremetal_client.list_nodes()
def _node_state_timeout(self, node_id, state_attr,
target_states, timeout=10, interval=1):
if not isinstance(target_states, list):
target_states = [target_states]
def check_state():
node = self.get_node(node_id=node_id)
if node.get(state_attr) in target_states:
return True
return False
if not test_utils.call_until_true(
check_state, timeout, interval):
msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
(node_id, state_attr, target_states))
raise lib_exc.TimeoutException(msg)
def wait_provisioning_state(self, node_id, state, timeout):
self._node_state_timeout(
node_id=node_id, state_attr='provision_state',
target_states=state, timeout=timeout)
def wait_power_state(self, node_id, state):
self._node_state_timeout(
node_id=node_id, state_attr='power_state',
target_states=state, timeout=CONF.baremetal.power_timeout)
def wait_node(self, instance_id):
"""Waits for a node to be associated with instance_id."""
def _get_node():
node = test_utils.call_and_ignore_notfound_exc(
self.get_node, instance_id=instance_id)
return node is not None
if not test_utils.call_until_true(
_get_node, CONF.baremetal.association_timeout, 1):
msg = ('Timed out waiting to get Ironic node by instance id %s'
% instance_id)
raise lib_exc.TimeoutException(msg)
def get_node(self, node_id=None, instance_id=None):
if node_id:
_, body = self.baremetal_client.show_node(node_id)
return body
elif instance_id:
_, body = self.baremetal_client.show_node_by_instance_uuid(
instance_id)
if body['nodes']:
return body['nodes'][0]
def get_ports(self, node_uuid):
ports = []
_, body = self.baremetal_client.list_node_ports(node_uuid)
for port in body['ports']:
_, p = self.baremetal_client.show_port(port['uuid'])
ports.append(p)
return ports
def add_keypair(self):
self.keypair = self.create_keypair()
def boot_instance(self):
self.instance = self.create_server(
key_name=self.keypair['name'])
self.wait_node(self.instance['id'])
self.node = self.get_node(instance_id=self.instance['id'])
self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
self.wait_provisioning_state(
self.node['uuid'],
[BaremetalProvisionStates.DEPLOYWAIT,
BaremetalProvisionStates.ACTIVE],
timeout=15)
self.wait_provisioning_state(self.node['uuid'],
BaremetalProvisionStates.ACTIVE,
timeout=CONF.baremetal.active_timeout)
waiters.wait_for_server_status(self.servers_client,
self.instance['id'], 'ACTIVE')
self.node = self.get_node(instance_id=self.instance['id'])
self.instance = (self.servers_client.show_server(self.instance['id'])
['server'])
def terminate_instance(self):
self.servers_client.delete_server(self.instance['id'])
self.wait_power_state(self.node['uuid'],
BaremetalPowerStates.POWER_OFF)
self.wait_provisioning_state(
self.node['uuid'],
BaremetalProvisionStates.NOSTATE,
timeout=CONF.baremetal.unprovision_timeout)
class EncryptionScenarioTest(ScenarioTest):
"""Base class for encryption scenario tests"""

View File

@ -1,102 +0,0 @@
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# 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 oslo_log import log as logging
from tempest.scenario import manager
from tempest import test
LOG = logging.getLogger(__name__)
class BaremetalBasicOps(manager.BaremetalScenarioTest):
"""This test tests the pxe_ssh Ironic driver.
It follows this basic set of operations:
* Creates a keypair
* Boots an instance using the keypair
* Monitors the associated Ironic node for power and
expected state transitions
* Validates Ironic node's port data has been properly updated
* Verifies SSH connectivity using created keypair via fixed IP
* Associates a floating ip
* Verifies SSH connectivity using created keypair via floating IP
* Deletes instance
* Monitors the associated Ironic node for power and
expected state transitions
"""
def verify_partition(self, client, label, mount, gib_size):
"""Verify a labeled partition's mount point and size."""
LOG.info("Looking for partition %s mounted on %s" % (label, mount))
# Validate we have a device with the given partition label
cmd = "/sbin/blkid | grep '%s' | cut -d':' -f1" % label
device = client.exec_command(cmd).rstrip('\n')
LOG.debug("Partition device is %s" % device)
self.assertNotEqual('', device)
# Validate the mount point for the device
cmd = "mount | grep '%s' | cut -d' ' -f3" % device
actual_mount = client.exec_command(cmd).rstrip('\n')
LOG.debug("Partition mount point is %s" % actual_mount)
self.assertEqual(actual_mount, mount)
# Validate the partition size matches what we expect
numbers = '0123456789'
devnum = device.replace('/dev/', '')
cmd = "cat /sys/block/%s/%s/size" % (devnum.rstrip(numbers), devnum)
num_bytes = client.exec_command(cmd).rstrip('\n')
num_bytes = int(num_bytes) * 512
actual_gib_size = num_bytes / (1024 * 1024 * 1024)
LOG.debug("Partition size is %d GiB" % actual_gib_size)
self.assertEqual(actual_gib_size, gib_size)
def get_flavor_ephemeral_size(self):
"""Returns size of the ephemeral partition in GiB."""
f_id = self.instance['flavor']['id']
flavor = self.flavors_client.show_flavor(f_id)['flavor']
ephemeral = flavor.get('OS-FLV-EXT-DATA:ephemeral')
if not ephemeral or ephemeral == 'N/A':
return None
return int(ephemeral)
def validate_ports(self):
for port in self.get_ports(self.node['uuid']):
n_port_id = port['extra']['vif_port_id']
body = self.ports_client.show_port(n_port_id)
n_port = body['port']
self.assertEqual(n_port['device_id'], self.instance['id'])
self.assertEqual(n_port['mac_address'], port['address'])
@test.idempotent_id('549173a5-38ec-42bb-b0e2-c8b9f4a08943')
@test.services('baremetal', 'compute', 'image', 'network')
def test_baremetal_server_ops(self):
self.add_keypair()
self.boot_instance()
self.validate_ports()
ip_address = self.get_server_ip(self.instance)
self.get_remote_client(ip_address).validate_authentication()
vm_client = self.get_remote_client(ip_address)
# We expect the ephemeral partition to be mounted on /mnt and to have
# the same size as our flavor definition.
eph_size = self.get_flavor_ephemeral_size()
if eph_size:
self.verify_partition(vm_client, 'ephemeral0', '/mnt', eph_size)
# Create the test file
self.create_timestamp(
ip_address, private_key=self.keypair['private_key'])
self.terminate_instance()

View File

@ -417,8 +417,9 @@ class TestNetworkBasicOps(manager.NetworkScenarioTest):
should_connect=True, mtu=self.network['mtu'])
@test.idempotent_id('1546850e-fbaa-42f5-8b5f-03d8a6a95f15')
@testtools.skipIf(CONF.baremetal.driver_enabled,
'Baremetal relies on a shared physical network.')
@testtools.skipIf(CONF.network.shared_physical_network,
'Connectivity can only be tested when in a '
'multitenant network environment')
@decorators.skip_because(bug="1610994")
@test.services('compute', 'network')
def test_connectivity_between_vms_on_different_networks(self):
@ -492,9 +493,9 @@ class TestNetworkBasicOps(manager.NetworkScenarioTest):
self._check_network_internal_connectivity(network=self.new_net)
@test.idempotent_id('04b9fe4e-85e8-4aea-b937-ea93885ac59f')
@testtools.skipIf(CONF.baremetal.driver_enabled,
'Router state cannot be altered on a shared baremetal '
'network')
@testtools.skipIf(CONF.network.shared_physical_network,
'Router state can be altered only with multitenant '
'networks capabilities')
@test.services('compute', 'network')
def test_update_router_admin_state(self):
"""Test to update admin state up of router
@ -524,8 +525,8 @@ class TestNetworkBasicOps(manager.NetworkScenarioTest):
"admin_state_up of router to True")
@test.idempotent_id('d8bb918e-e2df-48b2-97cd-b73c95450980')
@testtools.skipIf(CONF.baremetal.driver_enabled,
'network isolation not available for baremetal nodes')
@testtools.skipIf(CONF.network.shared_physical_network,
'network isolation not available')
@testtools.skipUnless(CONF.scenario.dhcp_client,
"DHCP client is not available.")
@test.services('compute', 'network')
@ -607,9 +608,6 @@ class TestNetworkBasicOps(manager.NetworkScenarioTest):
"new DNS nameservers")
@test.idempotent_id('f5dfcc22-45fd-409f-954c-5bd500d7890b')
@testtools.skipIf(CONF.baremetal.driver_enabled,
'admin_state of instance ports cannot be altered '
'for baremetal nodes')
@testtools.skipUnless(CONF.network_feature_enabled.port_admin_state_change,
"Changing a port's admin state is not supported "
"by the test environment")

View File

@ -50,8 +50,8 @@ class TestGettingAddress(manager.NetworkScenarioTest):
msg = ('Either project_networks_reachable must be "true", or '
'public_network_id must be defined.')
raise cls.skipException(msg)
if CONF.baremetal.driver_enabled:
msg = ('Baremetal does not currently support network isolation')
if not CONF.network.shared_physical_network:
msg = 'Deployment uses a shared physical network'
raise cls.skipException(msg)
@classmethod

View File

@ -130,9 +130,6 @@ class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
@classmethod
def skip_checks(cls):
super(TestSecurityGroupsBasicOps, cls).skip_checks()
if CONF.baremetal.driver_enabled:
msg = ('Not currently supported by baremetal.')
raise cls.skipException(msg)
if CONF.network.port_vnic_type in ['direct', 'macvtap']:
msg = ('Not currently supported when using vnic_type'
' direct or macvtap')
@ -145,6 +142,10 @@ class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
if not test.is_extension_enabled('security-group', 'network'):
msg = "security-group extension not enabled."
raise cls.skipException(msg)
if not CONF.network.shared_physical_network:
msg = ('Deployment uses a shared physical network, security '
'groups not supported')
raise cls.skipException(msg)
@classmethod
def setup_credentials(cls):

View File

@ -1,18 +0,0 @@
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
#
# 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 tempest.services.baremetal.v1.json.baremetal_client import \
BaremetalClient
__all__ = ['BaremetalClient']

View File

@ -1,205 +0,0 @@
# 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.
import functools
from oslo_serialization import jsonutils as json
import six
from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client
def handle_errors(f):
"""A decorator that allows to ignore certain types of errors."""
@functools.wraps(f)
def wrapper(*args, **kwargs):
param_name = 'ignore_errors'
ignored_errors = kwargs.get(param_name, tuple())
if param_name in kwargs:
del kwargs[param_name]
try:
return f(*args, **kwargs)
except ignored_errors:
# Silently ignore errors
pass
return wrapper
class BaremetalClient(rest_client.RestClient):
"""Base Tempest REST client for Ironic API."""
uri_prefix = ''
def serialize(self, object_dict):
"""Serialize an Ironic object."""
return json.dumps(object_dict)
def deserialize(self, object_str):
"""Deserialize an Ironic object."""
return json.loads(object_str)
def _get_uri(self, resource_name, uuid=None, permanent=False):
"""Get URI for a specific resource or object.
:param resource_name: The name of the REST resource, e.g., 'nodes'.
:param uuid: The unique identifier of an object in UUID format.
:returns: Relative URI for the resource or object.
"""
prefix = self.uri_prefix if not permanent else ''
return '{pref}/{res}{uuid}'.format(pref=prefix,
res=resource_name,
uuid='/%s' % uuid if uuid else '')
def _make_patch(self, allowed_attributes, **kwargs):
"""Create a JSON patch according to RFC 6902.
:param allowed_attributes: An iterable object that contains a set of
allowed attributes for an object.
:param **kwargs: Attributes and new values for them.
:returns: A JSON path that sets values of the specified attributes to
the new ones.
"""
def get_change(kwargs, path='/'):
for name, value in six.iteritems(kwargs):
if isinstance(value, dict):
for ch in get_change(value, path + '%s/' % name):
yield ch
else:
if value is None:
yield {'path': path + name,
'op': 'remove'}
else:
yield {'path': path + name,
'value': value,
'op': 'replace'}
patch = [ch for ch in get_change(kwargs)
if ch['path'].lstrip('/') in allowed_attributes]
return patch
def _list_request(self, resource, permanent=False, **kwargs):
"""Get the list of objects of the specified type.
:param resource: The name of the REST resource, e.g., 'nodes'.
:param **kwargs: Parameters for the request.
:returns: A tuple with the server response and deserialized JSON list
of objects
"""
uri = self._get_uri(resource, permanent=permanent)
if kwargs:
uri += "?%s" % urllib.urlencode(kwargs)
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return resp, self.deserialize(body)
def _show_request(self, resource, uuid, permanent=False, **kwargs):
"""Gets a specific object of the specified type.
:param uuid: Unique identifier of the object in UUID format.
:returns: Serialized object as a dictionary.
"""
if 'uri' in kwargs:
uri = kwargs['uri']
else:
uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return resp, self.deserialize(body)
def _create_request(self, resource, object_dict):
"""Create an object of the specified type.
:param resource: The name of the REST resource, e.g., 'nodes'.
:param object_dict: A Python dict that represents an object of the
specified type.
:returns: A tuple with the server response and the deserialized created
object.
"""
body = self.serialize(object_dict)
uri = self._get_uri(resource)
resp, body = self.post(uri, body=body)
self.expected_success(201, resp.status)
return resp, self.deserialize(body)
def _delete_request(self, resource, uuid):
"""Delete specified object.
:param resource: The name of the REST resource, e.g., 'nodes'.
:param uuid: The unique identifier of an object in UUID format.
:returns: A tuple with the server response and the response body.
"""
uri = self._get_uri(resource, uuid)
resp, body = self.delete(uri)
self.expected_success(204, resp.status)
return resp, body
def _patch_request(self, resource, uuid, patch_object):
"""Update specified object with JSON-patch.
:param resource: The name of the REST resource, e.g., 'nodes'.
:param uuid: The unique identifier of an object in UUID format.
:returns: A tuple with the server response and the serialized patched
object.
"""
uri = self._get_uri(resource, uuid)
patch_body = json.dumps(patch_object)
resp, body = self.patch(uri, body=patch_body)
self.expected_success(200, resp.status)
return resp, self.deserialize(body)
@handle_errors
def get_api_description(self):
"""Retrieves all versions of the Ironic API."""
return self._list_request('', permanent=True)
@handle_errors
def get_version_description(self, version='v1'):
"""Retrieves the desctription of the API.
:param version: The version of the API. Default: 'v1'.
:returns: Serialized description of API resources.
"""
return self._list_request(version, permanent=True)
def _put_request(self, resource, put_object):
"""Update specified object with JSON-patch."""
uri = self._get_uri(resource)
put_body = json.dumps(put_object)
resp, body = self.put(uri, body=put_body)
self.expected_success([202, 204], resp.status)
return resp, body

View File

@ -1,362 +0,0 @@
# 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 tempest.services.baremetal import base
class BaremetalClient(base.BaremetalClient):
"""Base Tempest REST client for Ironic API v1."""
version = '1'
uri_prefix = 'v1'
@base.handle_errors
def list_nodes(self, **kwargs):
"""List all existing nodes.
Available params: see http://developer.openstack.org/api-ref/
baremetal/index.html#list-nodes
"""
return self._list_request('nodes', **kwargs)
@base.handle_errors
def list_chassis(self):
"""List all existing chassis."""
return self._list_request('chassis')
@base.handle_errors
def list_chassis_nodes(self, chassis_uuid):
"""List all nodes associated with a chassis."""
return self._list_request('/chassis/%s/nodes' % chassis_uuid)
@base.handle_errors
def list_ports(self, **kwargs):
"""List all existing ports.
Available params: see http://developer.openstack.org/api-ref/
baremetal/index.html?expanded=#list-ports
"""
return self._list_request('ports', **kwargs)
@base.handle_errors
def list_node_ports(self, uuid):
"""List all ports associated with the node."""
return self._list_request('/nodes/%s/ports' % uuid)
@base.handle_errors
def list_nodestates(self, uuid):
"""List all existing states."""
return self._list_request('/nodes/%s/states' % uuid)
@base.handle_errors
def list_ports_detail(self, **kwargs):
"""Details list all existing ports.
Available params: see http://developer.openstack.org/api-ref/baremetal/
index.html?expanded=#list-detailed-ports
"""
return self._list_request('/ports/detail', **kwargs)
@base.handle_errors
def list_drivers(self):
"""List all existing drivers."""
return self._list_request('drivers')
@base.handle_errors
def show_node(self, uuid):
"""Gets a specific node.
:param uuid: Unique identifier of the node in UUID format.
:return: Serialized node as a dictionary.
"""
return self._show_request('nodes', uuid)
@base.handle_errors
def show_node_by_instance_uuid(self, instance_uuid):
"""Gets a node associated with given instance uuid.
:param instance_uuid: Unique identifier of the instance in UUID format.
:return: Serialized node as a dictionary.
"""
uri = '/nodes/detail?instance_uuid=%s' % instance_uuid
return self._show_request('nodes',
uuid=None,
uri=uri)
@base.handle_errors
def show_chassis(self, uuid):
"""Gets a specific chassis.
:param uuid: Unique identifier of the chassis in UUID format.
:return: Serialized chassis as a dictionary.
"""
return self._show_request('chassis', uuid)
@base.handle_errors
def show_port(self, uuid):
"""Gets a specific port.
:param uuid: Unique identifier of the port in UUID format.
:return: Serialized port as a dictionary.
"""
return self._show_request('ports', uuid)
@base.handle_errors
def show_port_by_address(self, address):
"""Gets a specific port by address.
:param address: MAC address of the port.
:return: Serialized port as a dictionary.
"""
uri = '/ports/detail?address=%s' % address
return self._show_request('ports', uuid=None, uri=uri)
def show_driver(self, driver_name):
"""Gets a specific driver.
:param driver_name: Name of driver.
:return: Serialized driver as a dictionary.
"""
return self._show_request('drivers', driver_name)
@base.handle_errors
def create_node(self, chassis_id=None, **kwargs):
"""Create a baremetal node with the specified parameters.
:param chassis_id: The unique identifier of the chassis.
:param cpu_arch: CPU architecture of the node. Default: x86_64.
:param cpus: Number of CPUs. Default: 8.
:param local_gb: Disk size. Default: 1024.
:param memory_mb: Available RAM. Default: 4096.
:param driver: Driver name. Default: "fake"
:return: A tuple with the server response and the created node.
"""
node = {'chassis_uuid': chassis_id,
'properties': {'cpu_arch': kwargs.get('cpu_arch', 'x86_64'),
'cpus': kwargs.get('cpus', 8),
'local_gb': kwargs.get('local_gb', 1024),
'memory_mb': kwargs.get('memory_mb', 4096)},
'driver': kwargs.get('driver', 'fake')}
return self._create_request('nodes', node)
@base.handle_errors
def create_chassis(self, **kwargs):
"""Create a chassis with the specified parameters.
:param description: The description of the chassis.
Default: test-chassis
:return: A tuple with the server response and the created chassis.
"""
chassis = {'description': kwargs.get('description', 'test-chassis')}
return self._create_request('chassis', chassis)
@base.handle_errors
def create_port(self, node_id, **kwargs):
"""Create a port with the specified parameters.
:param node_id: The ID of the node which owns the port.
:param address: MAC address of the port.
:param extra: Meta data of the port. Default: {'foo': 'bar'}.
:param uuid: UUID of the port.
:return: A tuple with the server response and the created port.
"""
port = {'extra': kwargs.get('extra', {'foo': 'bar'}),
'uuid': kwargs['uuid']}
if node_id is not None:
port['node_uuid'] = node_id
if kwargs['address'] is not None:
port['address'] = kwargs['address']
return self._create_request('ports', port)
@base.handle_errors
def delete_node(self, uuid):
"""Deletes a node having the specified UUID.
:param uuid: The unique identifier of the node.
:return: A tuple with the server response and the response body.
"""
return self._delete_request('nodes', uuid)
@base.handle_errors
def delete_chassis(self, uuid):
"""Deletes a chassis having the specified UUID.
:param uuid: The unique identifier of the chassis.
:return: A tuple with the server response and the response body.
"""
return self._delete_request('chassis', uuid)
@base.handle_errors
def delete_port(self, uuid):
"""Deletes a port having the specified UUID.
:param uuid: The unique identifier of the port.
:return: A tuple with the server response and the response body.
"""
return self._delete_request('ports', uuid)
@base.handle_errors
def update_node(self, uuid, **kwargs):
"""Update the specified node.
:param uuid: The unique identifier of the node.
:return: A tuple with the server response and the updated node.
"""
node_attributes = ('properties/cpu_arch',
'properties/cpus',
'properties/local_gb',
'properties/memory_mb',
'driver',
'instance_uuid')
patch = self._make_patch(node_attributes, **kwargs)
return self._patch_request('nodes', uuid, patch)
@base.handle_errors
def update_chassis(self, uuid, **kwargs):
"""Update the specified chassis.
:param uuid: The unique identifier of the chassis.
:return: A tuple with the server response and the updated chassis.
"""
chassis_attributes = ('description',)
patch = self._make_patch(chassis_attributes, **kwargs)
return self._patch_request('chassis', uuid, patch)
@base.handle_errors
def update_port(self, uuid, patch):
"""Update the specified port.
:param uuid: The unique identifier of the port.
:param patch: List of dicts representing json patches.
:return: A tuple with the server response and the updated port.
"""
return self._patch_request('ports', uuid, patch)
@base.handle_errors
def set_node_power_state(self, node_uuid, state):
"""Set power state of the specified node.
:param node_uuid: The unique identifier of the node.
:param state: desired state to set (on/off/reboot).
"""
target = {'target': state}
return self._put_request('nodes/%s/states/power' % node_uuid,
target)
@base.handle_errors
def validate_driver_interface(self, node_uuid):
"""Get all driver interfaces of a specific node.
:param node_uuid: Unique identifier of the node in UUID format.
"""
uri = '{pref}/{res}/{uuid}/{postf}'.format(pref=self.uri_prefix,
res='nodes',
uuid=node_uuid,
postf='validate')
return self._show_request('nodes', node_uuid, uri=uri)
@base.handle_errors
def set_node_boot_device(self, node_uuid, boot_device, persistent=False):
"""Set the boot device of the specified node.
:param node_uuid: The unique identifier of the node.
:param boot_device: The boot device name.
:param persistent: Boolean value. True if the boot device will
persist to all future boots, False if not.
Default: False.
"""
request = {'boot_device': boot_device, 'persistent': persistent}
resp, body = self._put_request('nodes/%s/management/boot_device' %
node_uuid, request)
self.expected_success(204, resp.status)
return body
@base.handle_errors
def get_node_boot_device(self, node_uuid):
"""Get the current boot device of the specified node.
:param node_uuid: The unique identifier of the node.
"""
path = 'nodes/%s/management/boot_device' % node_uuid
resp, body = self._list_request(path)
self.expected_success(200, resp.status)
return body
@base.handle_errors
def get_node_supported_boot_devices(self, node_uuid):
"""Get the supported boot devices of the specified node.
:param node_uuid: The unique identifier of the node.
"""
path = 'nodes/%s/management/boot_device/supported' % node_uuid
resp, body = self._list_request(path)
self.expected_success(200, resp.status)
return body
@base.handle_errors
def get_console(self, node_uuid):
"""Get connection information about the console.
:param node_uuid: Unique identifier of the node in UUID format.
"""
resp, body = self._show_request('nodes/states/console', node_uuid)
self.expected_success(200, resp.status)
return resp, body
@base.handle_errors
def set_console_mode(self, node_uuid, enabled):
"""Start and stop the node console.
:param node_uuid: Unique identifier of the node in UUID format.
:param enabled: Boolean value; whether to enable or disable the
console.
"""
enabled = {'enabled': enabled}
resp, body = self._put_request('nodes/%s/states/console' % node_uuid,
enabled)
self.expected_success(202, resp.status)
return resp, body

View File

@ -64,7 +64,6 @@ def get_service_list():
service_list = {
'compute': CONF.service_available.nova,
'image': CONF.service_available.glance,
'baremetal': CONF.service_available.ironic,
'volume': CONF.service_available.cinder,
'network': True,
'identity': True,