blob: 2d3f190092d1c89e5175f1e384ea66ecd365965b [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
Fei Long Wangd39431f2015-05-14 11:30:48 +120015from tempest.common.utils import data_utils
Matthew Treinishcb09bbb2014-01-29 18:20:25 +000016from tempest import config
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050017from tempest.lib import exceptions as lib_exc
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030018from tempest import test
19
Matthew Treinishcb09bbb2014-01-29 18:20:25 +000020CONF = config.CONF
21
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030022
Adam Gandelman3e8f7962014-06-25 13:18:29 -070023# NOTE(adam_g): The baremetal API tests exercise operations such as enroll
24# node, power on, power off, etc. Testing against real drivers (ie, IPMI)
25# will require passing driver-specific data to Tempest (addresses,
26# credentials, etc). Until then, only support testing against the fake driver,
27# which has no external dependencies.
28SUPPORTED_DRIVERS = ['fake']
29
Jim Rollenhagen92420f02014-09-19 12:04:07 -070030# NOTE(jroll): resources must be deleted in a specific order, this list
31# defines the resource types to clean up, and the correct order.
32RESOURCE_TYPES = ['port', 'node', 'chassis']
33
Adam Gandelman3e8f7962014-06-25 13:18:29 -070034
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030035def creates(resource):
36 """Decorator that adds resources to the appropriate cleanup list."""
37
38 def decorator(f):
39 @functools.wraps(f)
40 def wrapper(cls, *args, **kwargs):
Mh Raiesa9bb79d2014-04-17 16:20:17 +053041 resp, body = f(cls, *args, **kwargs)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030042
43 if 'uuid' in body:
44 cls.created_objects[resource].add(body['uuid'])
45
Mh Raiesa9bb79d2014-04-17 16:20:17 +053046 return resp, body
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030047 return wrapper
48 return decorator
49
50
51class BaseBaremetalTest(test.BaseTestCase):
52 """Base class for Baremetal API tests."""
53
Andrea Frittolib21de6c2015-02-06 20:12:38 +000054 credentials = ['admin']
55
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030056 @classmethod
Rohan Kanade7ed42f52015-02-03 13:00:29 +053057 def skip_checks(cls):
58 super(BaseBaremetalTest, cls).skip_checks()
Matthew Treinishcb09bbb2014-01-29 18:20:25 +000059 if not CONF.service_available.ironic:
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030060 skip_msg = ('%s skipped as Ironic is not available' % cls.__name__)
61 raise cls.skipException(skip_msg)
62
Adam Gandelman3e8f7962014-06-25 13:18:29 -070063 if CONF.baremetal.driver not in SUPPORTED_DRIVERS:
64 skip_msg = ('%s skipped as Ironic driver %s is not supported for '
65 'testing.' %
66 (cls.__name__, CONF.baremetal.driver))
67 raise cls.skipException(skip_msg)
Adam Gandelman3e8f7962014-06-25 13:18:29 -070068
Rohan Kanade7ed42f52015-02-03 13:00:29 +053069 @classmethod
Rohan Kanade7ed42f52015-02-03 13:00:29 +053070 def setup_clients(cls):
71 super(BaseBaremetalTest, cls).setup_clients()
Andrea Frittolib21de6c2015-02-06 20:12:38 +000072 cls.client = cls.os_admin.baremetal_client
Rohan Kanade7ed42f52015-02-03 13:00:29 +053073
74 @classmethod
75 def resource_setup(cls):
76 super(BaseBaremetalTest, cls).resource_setup()
77
78 cls.driver = CONF.baremetal.driver
Mh Raiesf8ecf232014-04-17 12:43:55 +053079 cls.power_timeout = CONF.baremetal.power_timeout
Jim Rollenhagen92420f02014-09-19 12:04:07 -070080 cls.created_objects = {}
81 for resource in RESOURCE_TYPES:
82 cls.created_objects[resource] = set()
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030083
84 @classmethod
Andrea Frittoliba240c32014-09-15 13:14:53 +010085 def resource_cleanup(cls):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030086 """Ensure that all created objects get destroyed."""
87
88 try:
Jim Rollenhagen92420f02014-09-19 12:04:07 -070089 for resource in RESOURCE_TYPES:
90 uuids = cls.created_objects[resource]
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030091 delete_method = getattr(cls.client, 'delete_%s' % resource)
92 for u in uuids:
Masayuki Igawabfa07602015-01-20 18:47:17 +090093 delete_method(u, ignore_errors=lib_exc.NotFound)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030094 finally:
Andrea Frittoliba240c32014-09-15 13:14:53 +010095 super(BaseBaremetalTest, cls).resource_cleanup()
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030096
97 @classmethod
98 @creates('chassis')
guo yunxian03154ac2016-07-29 20:32:11 +080099 def create_chassis(cls, description=None):
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000100 """Wrapper utility for creating test chassis.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300101
102 :param description: A description of the chassis. if not supplied,
103 a random value will be generated.
104 :return: Created chassis.
105
106 """
Ken'ichi Ohmichi823d3312015-03-23 00:27:53 +0000107 description = description or data_utils.rand_name('test-chassis')
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300108 resp, body = cls.client.create_chassis(description=description)
Mh Raiesa9bb79d2014-04-17 16:20:17 +0530109 return resp, body
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300110
111 @classmethod
112 @creates('node')
Adam Gandelman3ea1eb82015-02-18 19:13:25 -0800113 def create_node(cls, chassis_id, cpu_arch='x86', cpus=8, local_gb=10,
114 memory_mb=4096):
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000115 """Wrapper utility for creating test baremetal nodes.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300116
117 :param cpu_arch: CPU architecture of the node. Default: x86.
Adam Gandelman3ea1eb82015-02-18 19:13:25 -0800118 :param cpus: Number of CPUs. Default: 8.
119 :param local_gb: Disk size. Default: 10.
120 :param memory_mb: Available RAM. Default: 4096.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300121 :return: Created node.
122
123 """
124 resp, body = cls.client.create_node(chassis_id, cpu_arch=cpu_arch,
Adam Gandelman3ea1eb82015-02-18 19:13:25 -0800125 cpus=cpus, local_gb=local_gb,
126 memory_mb=memory_mb,
127 driver=cls.driver)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300128
Mh Raiesa9bb79d2014-04-17 16:20:17 +0530129 return resp, body
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300130
131 @classmethod
132 @creates('port')
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400133 def create_port(cls, node_id, address, extra=None, uuid=None):
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000134 """Wrapper utility for creating test ports.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300135
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400136 :param address: MAC address of the port.
137 :param extra: Meta data of the port. If not supplied, an empty
138 dictionary will be created.
139 :param uuid: UUID of the port.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300140 :return: Created port.
141
142 """
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400143 extra = extra or {}
144 resp, body = cls.client.create_port(address=address, node_id=node_id,
145 extra=extra, uuid=uuid)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300146
Mh Raiesa9bb79d2014-04-17 16:20:17 +0530147 return resp, body
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300148
149 @classmethod
150 def delete_chassis(cls, chassis_id):
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000151 """Deletes a chassis having the specified UUID.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300152
153 :param uuid: The unique identifier of the chassis.
154 :return: Server response.
155
156 """
157
158 resp, body = cls.client.delete_chassis(chassis_id)
159
160 if chassis_id in cls.created_objects['chassis']:
161 cls.created_objects['chassis'].remove(chassis_id)
162
163 return resp
164
165 @classmethod
166 def delete_node(cls, node_id):
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000167 """Deletes a node having the specified UUID.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300168
169 :param uuid: The unique identifier of the node.
170 :return: Server response.
171
172 """
173
174 resp, body = cls.client.delete_node(node_id)
175
176 if node_id in cls.created_objects['node']:
177 cls.created_objects['node'].remove(node_id)
178
179 return resp
180
181 @classmethod
182 def delete_port(cls, port_id):
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000183 """Deletes a port having the specified UUID.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300184
185 :param uuid: The unique identifier of the port.
186 :return: Server response.
187
188 """
189
190 resp, body = cls.client.delete_port(port_id)
191
192 if port_id in cls.created_objects['port']:
193 cls.created_objects['port'].remove(port_id)
194
195 return resp
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400196
197 def validate_self_link(self, resource, uuid, link):
198 """Check whether the given self link formatted correctly."""
199 expected_link = "{base}/{pref}/{res}/{uuid}".format(
200 base=self.client.base_url,
201 pref=self.client.uri_prefix,
202 res=resource,
203 uuid=uuid)
204 self.assertEqual(expected_link, link)