blob: 9aeea0ae910a02b9ed7ce15bcca2dfc4100a1bd0 [file] [log] [blame]
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +03001# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13import functools
Matthew Treinish01472ff2015-02-20 17:26:52 -050014
15from tempest_lib.common.utils import data_utils
Masayuki Igawabfa07602015-01-20 18:47:17 +090016from tempest_lib import exceptions as lib_exc
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030017
18from tempest import clients
Matthew Treinishdb9721d2015-03-18 14:21:28 -040019from tempest.common import credentials
Matthew Treinishcb09bbb2014-01-29 18:20:25 +000020from tempest import config
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030021from tempest import test
22
Matthew Treinishcb09bbb2014-01-29 18:20:25 +000023CONF = config.CONF
24
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030025
Adam Gandelman3e8f7962014-06-25 13:18:29 -070026# NOTE(adam_g): The baremetal API tests exercise operations such as enroll
27# node, power on, power off, etc. Testing against real drivers (ie, IPMI)
28# will require passing driver-specific data to Tempest (addresses,
29# credentials, etc). Until then, only support testing against the fake driver,
30# which has no external dependencies.
31SUPPORTED_DRIVERS = ['fake']
32
Jim Rollenhagen92420f02014-09-19 12:04:07 -070033# NOTE(jroll): resources must be deleted in a specific order, this list
34# defines the resource types to clean up, and the correct order.
35RESOURCE_TYPES = ['port', 'node', 'chassis']
36
Adam Gandelman3e8f7962014-06-25 13:18:29 -070037
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030038def creates(resource):
39 """Decorator that adds resources to the appropriate cleanup list."""
40
41 def decorator(f):
42 @functools.wraps(f)
43 def wrapper(cls, *args, **kwargs):
Mh Raiesa9bb79d2014-04-17 16:20:17 +053044 resp, body = f(cls, *args, **kwargs)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030045
46 if 'uuid' in body:
47 cls.created_objects[resource].add(body['uuid'])
48
Mh Raiesa9bb79d2014-04-17 16:20:17 +053049 return resp, body
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030050 return wrapper
51 return decorator
52
53
54class BaseBaremetalTest(test.BaseTestCase):
55 """Base class for Baremetal API tests."""
56
57 @classmethod
Rohan Kanade7ed42f52015-02-03 13:00:29 +053058 def skip_checks(cls):
59 super(BaseBaremetalTest, cls).skip_checks()
Matthew Treinishcb09bbb2014-01-29 18:20:25 +000060 if not CONF.service_available.ironic:
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030061 skip_msg = ('%s skipped as Ironic is not available' % cls.__name__)
62 raise cls.skipException(skip_msg)
63
Adam Gandelman3e8f7962014-06-25 13:18:29 -070064 if CONF.baremetal.driver not in SUPPORTED_DRIVERS:
65 skip_msg = ('%s skipped as Ironic driver %s is not supported for '
66 'testing.' %
67 (cls.__name__, CONF.baremetal.driver))
68 raise cls.skipException(skip_msg)
Adam Gandelman3e8f7962014-06-25 13:18:29 -070069
Rohan Kanade7ed42f52015-02-03 13:00:29 +053070 @classmethod
71 def setup_credentials(cls):
72 super(BaseBaremetalTest, cls).setup_credentials()
Matthew Treinishdb9721d2015-03-18 14:21:28 -040073 if (not hasattr(cls, 'isolated_creds') or
74 not cls.isolated_creds.name == cls.__name__):
75 cls.isolated_creds = credentials.get_isolated_credentials(
76 name=cls.__name__, network_resources=cls.network_resources)
77 cls.mgr = clients.Manager(cls.isolated_creds.get_admin_creds())
Rohan Kanade7ed42f52015-02-03 13:00:29 +053078
79 @classmethod
80 def setup_clients(cls):
81 super(BaseBaremetalTest, cls).setup_clients()
82 cls.client = cls.mgr.baremetal_client
83
84 @classmethod
85 def resource_setup(cls):
86 super(BaseBaremetalTest, cls).resource_setup()
87
88 cls.driver = CONF.baremetal.driver
Mh Raiesf8ecf232014-04-17 12:43:55 +053089 cls.power_timeout = CONF.baremetal.power_timeout
Jim Rollenhagen92420f02014-09-19 12:04:07 -070090 cls.created_objects = {}
91 for resource in RESOURCE_TYPES:
92 cls.created_objects[resource] = set()
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030093
94 @classmethod
Andrea Frittoliba240c32014-09-15 13:14:53 +010095 def resource_cleanup(cls):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030096 """Ensure that all created objects get destroyed."""
97
98 try:
Jim Rollenhagen92420f02014-09-19 12:04:07 -070099 for resource in RESOURCE_TYPES:
100 uuids = cls.created_objects[resource]
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300101 delete_method = getattr(cls.client, 'delete_%s' % resource)
102 for u in uuids:
Masayuki Igawabfa07602015-01-20 18:47:17 +0900103 delete_method(u, ignore_errors=lib_exc.NotFound)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300104 finally:
Andrea Frittoliba240c32014-09-15 13:14:53 +0100105 super(BaseBaremetalTest, cls).resource_cleanup()
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300106
107 @classmethod
108 @creates('chassis')
109 def create_chassis(cls, description=None, expect_errors=False):
110 """
111 Wrapper utility for creating test chassis.
112
113 :param description: A description of the chassis. if not supplied,
114 a random value will be generated.
115 :return: Created chassis.
116
117 """
Ken'ichi Ohmichi823d3312015-03-23 00:27:53 +0000118 description = description or data_utils.rand_name('test-chassis')
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300119 resp, body = cls.client.create_chassis(description=description)
Mh Raiesa9bb79d2014-04-17 16:20:17 +0530120 return resp, body
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300121
122 @classmethod
123 @creates('node')
Adam Gandelman3ea1eb82015-02-18 19:13:25 -0800124 def create_node(cls, chassis_id, cpu_arch='x86', cpus=8, local_gb=10,
125 memory_mb=4096):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300126 """
127 Wrapper utility for creating test baremetal nodes.
128
129 :param cpu_arch: CPU architecture of the node. Default: x86.
Adam Gandelman3ea1eb82015-02-18 19:13:25 -0800130 :param cpus: Number of CPUs. Default: 8.
131 :param local_gb: Disk size. Default: 10.
132 :param memory_mb: Available RAM. Default: 4096.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300133 :return: Created node.
134
135 """
136 resp, body = cls.client.create_node(chassis_id, cpu_arch=cpu_arch,
Adam Gandelman3ea1eb82015-02-18 19:13:25 -0800137 cpus=cpus, local_gb=local_gb,
138 memory_mb=memory_mb,
139 driver=cls.driver)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300140
Mh Raiesa9bb79d2014-04-17 16:20:17 +0530141 return resp, body
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300142
143 @classmethod
144 @creates('port')
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400145 def create_port(cls, node_id, address, extra=None, uuid=None):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300146 """
147 Wrapper utility for creating test ports.
148
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400149 :param address: MAC address of the port.
150 :param extra: Meta data of the port. If not supplied, an empty
151 dictionary will be created.
152 :param uuid: UUID of the port.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300153 :return: Created port.
154
155 """
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400156 extra = extra or {}
157 resp, body = cls.client.create_port(address=address, node_id=node_id,
158 extra=extra, uuid=uuid)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300159
Mh Raiesa9bb79d2014-04-17 16:20:17 +0530160 return resp, body
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300161
162 @classmethod
163 def delete_chassis(cls, chassis_id):
164 """
165 Deletes a chassis having the specified UUID.
166
167 :param uuid: The unique identifier of the chassis.
168 :return: Server response.
169
170 """
171
172 resp, body = cls.client.delete_chassis(chassis_id)
173
174 if chassis_id in cls.created_objects['chassis']:
175 cls.created_objects['chassis'].remove(chassis_id)
176
177 return resp
178
179 @classmethod
180 def delete_node(cls, node_id):
181 """
182 Deletes a node having the specified UUID.
183
184 :param uuid: The unique identifier of the node.
185 :return: Server response.
186
187 """
188
189 resp, body = cls.client.delete_node(node_id)
190
191 if node_id in cls.created_objects['node']:
192 cls.created_objects['node'].remove(node_id)
193
194 return resp
195
196 @classmethod
197 def delete_port(cls, port_id):
198 """
199 Deletes a port having the specified UUID.
200
201 :param uuid: The unique identifier of the port.
202 :return: Server response.
203
204 """
205
206 resp, body = cls.client.delete_port(port_id)
207
208 if port_id in cls.created_objects['port']:
209 cls.created_objects['port'].remove(port_id)
210
211 return resp
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400212
213 def validate_self_link(self, resource, uuid, link):
214 """Check whether the given self link formatted correctly."""
215 expected_link = "{base}/{pref}/{res}/{uuid}".format(
216 base=self.client.base_url,
217 pref=self.client.uri_prefix,
218 res=resource,
219 uuid=uuid)
220 self.assertEqual(expected_link, link)