blob: 6e248014df17f4ba0ce14644fcc0457a33479204 [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
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030014
Matthew Treinish21905512015-07-13 10:33:35 -040015from oslo_serialization import jsonutils as json
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030016import six
Matthew Treinish89128142015-04-23 10:44:30 -040017from six.moves.urllib import parse as urllib
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030018
Ken'ichi Ohmichi322e4432016-03-01 15:52:09 -080019from tempest.lib.common import rest_client
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030020
21
22def handle_errors(f):
23 """A decorator that allows to ignore certain types of errors."""
24
25 @functools.wraps(f)
26 def wrapper(*args, **kwargs):
27 param_name = 'ignore_errors'
28 ignored_errors = kwargs.get(param_name, tuple())
29
30 if param_name in kwargs:
31 del kwargs[param_name]
32
33 try:
34 return f(*args, **kwargs)
35 except ignored_errors:
36 # Silently ignore errors
37 pass
38
39 return wrapper
40
41
Ken'ichi Ohmichi322e4432016-03-01 15:52:09 -080042class BaremetalClient(rest_client.RestClient):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +000043 """Base Tempest REST client for Ironic API."""
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030044
Ken'ichi Ohmichi1f88ece2015-01-23 03:33:11 +000045 uri_prefix = ''
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030046
Ken'ichi Ohmichi19b6ff52015-01-23 02:45:50 +000047 def serialize(self, object_dict):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030048 """Serialize an Ironic object."""
49
Ken'ichi Ohmichi19b6ff52015-01-23 02:45:50 +000050 return json.dumps(object_dict)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030051
52 def deserialize(self, object_str):
53 """Deserialize an Ironic object."""
54
Ken'ichi Ohmichi19b6ff52015-01-23 02:45:50 +000055 return json.loads(object_str)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030056
57 def _get_uri(self, resource_name, uuid=None, permanent=False):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +000058 """Get URI for a specific resource or object.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030059
60 :param resource_name: The name of the REST resource, e.g., 'nodes'.
61 :param uuid: The unique identifier of an object in UUID format.
lei zhangac9969a2015-11-28 17:01:17 +080062 :returns: Relative URI for the resource or object.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030063
64 """
65 prefix = self.uri_prefix if not permanent else ''
66
67 return '{pref}/{res}{uuid}'.format(pref=prefix,
68 res=resource_name,
69 uuid='/%s' % uuid if uuid else '')
70
lei zhangac9969a2015-11-28 17:01:17 +080071 def _make_patch(self, allowed_attributes, **kwargs):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +000072 """Create a JSON patch according to RFC 6902.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030073
74 :param allowed_attributes: An iterable object that contains a set of
75 allowed attributes for an object.
lei zhangac9969a2015-11-28 17:01:17 +080076 :param **kwargs: Attributes and new values for them.
77 :returns: A JSON path that sets values of the specified attributes to
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030078 the new ones.
79
80 """
lei zhangac9969a2015-11-28 17:01:17 +080081 def get_change(kwargs, path='/'):
82 for name, value in six.iteritems(kwargs):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030083 if isinstance(value, dict):
84 for ch in get_change(value, path + '%s/' % name):
85 yield ch
86 else:
Adam Gandelman00682612014-09-02 17:10:36 -070087 if value is None:
88 yield {'path': path + name,
89 'op': 'remove'}
90 else:
91 yield {'path': path + name,
92 'value': value,
93 'op': 'replace'}
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030094
lei zhangac9969a2015-11-28 17:01:17 +080095 patch = [ch for ch in get_change(kwargs)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030096 if ch['path'].lstrip('/') in allowed_attributes]
97
98 return patch
99
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400100 def _list_request(self, resource, permanent=False, **kwargs):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +0000101 """Get the list of objects of the specified type.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300102
103 :param resource: The name of the REST resource, e.g., 'nodes'.
lei zhangac9969a2015-11-28 17:01:17 +0800104 :param **kwargs: Parameters for the request.
105 :returns: A tuple with the server response and deserialized JSON list
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300106 of objects
107
108 """
109 uri = self._get_uri(resource, permanent=permanent)
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400110 if kwargs:
111 uri += "?%s" % urllib.urlencode(kwargs)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300112
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200113 resp, body = self.get(uri)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000114 self.expected_success(200, resp['status'])
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300115
116 return resp, self.deserialize(body)
117
raiesmh08e5d84572014-06-23 09:49:03 +0530118 def _show_request(self, resource, uuid, permanent=False, **kwargs):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +0000119 """Gets a specific object of the specified type.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300120
121 :param uuid: Unique identifier of the object in UUID format.
lei zhangac9969a2015-11-28 17:01:17 +0800122 :returns: Serialized object as a dictionary.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300123
124 """
raiesmh08e5d84572014-06-23 09:49:03 +0530125 if 'uri' in kwargs:
126 uri = kwargs['uri']
127 else:
128 uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200129 resp, body = self.get(uri)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000130 self.expected_success(200, resp['status'])
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300131
132 return resp, self.deserialize(body)
133
Ken'ichi Ohmichi19b6ff52015-01-23 02:45:50 +0000134 def _create_request(self, resource, object_dict):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +0000135 """Create an object of the specified type.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300136
137 :param resource: The name of the REST resource, e.g., 'nodes'.
138 :param object_dict: A Python dict that represents an object of the
139 specified type.
lei zhangac9969a2015-11-28 17:01:17 +0800140 :returns: A tuple with the server response and the deserialized created
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300141 object.
142
143 """
Ken'ichi Ohmichi19b6ff52015-01-23 02:45:50 +0000144 body = self.serialize(object_dict)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300145 uri = self._get_uri(resource)
146
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200147 resp, body = self.post(uri, body=body)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000148 self.expected_success(201, resp['status'])
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300149
150 return resp, self.deserialize(body)
151
152 def _delete_request(self, resource, uuid):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +0000153 """Delete specified object.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300154
155 :param resource: The name of the REST resource, e.g., 'nodes'.
156 :param uuid: The unique identifier of an object in UUID format.
lei zhangac9969a2015-11-28 17:01:17 +0800157 :returns: A tuple with the server response and the response body.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300158
159 """
160 uri = self._get_uri(resource, uuid)
161
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200162 resp, body = self.delete(uri)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000163 self.expected_success(204, resp['status'])
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300164 return resp, body
165
166 def _patch_request(self, resource, uuid, patch_object):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +0000167 """Update specified object with JSON-patch.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300168
169 :param resource: The name of the REST resource, e.g., 'nodes'.
170 :param uuid: The unique identifier of an object in UUID format.
lei zhangac9969a2015-11-28 17:01:17 +0800171 :returns: A tuple with the server response and the serialized patched
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300172 object.
173
174 """
175 uri = self._get_uri(resource, uuid)
176 patch_body = json.dumps(patch_object)
177
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200178 resp, body = self.patch(uri, body=patch_body)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000179 self.expected_success(200, resp['status'])
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300180 return resp, self.deserialize(body)
181
182 @handle_errors
183 def get_api_description(self):
184 """Retrieves all versions of the Ironic API."""
185
186 return self._list_request('', permanent=True)
187
188 @handle_errors
189 def get_version_description(self, version='v1'):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +0000190 """Retrieves the desctription of the API.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300191
192 :param version: The version of the API. Default: 'v1'.
lei zhangac9969a2015-11-28 17:01:17 +0800193 :returns: Serialized description of API resources.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300194
195 """
196 return self._list_request(version, permanent=True)
Mh Raiesf8ecf232014-04-17 12:43:55 +0530197
198 def _put_request(self, resource, put_object):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +0000199 """Update specified object with JSON-patch."""
Mh Raiesf8ecf232014-04-17 12:43:55 +0530200 uri = self._get_uri(resource)
201 put_body = json.dumps(put_object)
202
203 resp, body = self.put(uri, body=put_body)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000204 self.expected_success(202, resp['status'])
Mh Raiesf8ecf232014-04-17 12:43:55 +0530205 return resp, body