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:
parent
bc80debd90
commit
66cded2553
tempest
api
clients.pycommon
config.pyscenario
manager.pytest_baremetal_basic_ops.pytest_network_basic_ops.pytest_network_v6.pytest_security_groups_basic_ops.py
services/baremetal
test.py@ -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).
|
@ -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)
|
@ -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)
|
@ -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']])
|
@ -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'])
|
@ -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']])
|
@ -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)
|
@ -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'])
|
@ -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'])
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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"""
|
||||
|
||||
|
@ -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()
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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']
|
@ -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
|
@ -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
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user