blob: 2ac3fb2fdc3c3d66801ffae26f7d4386af4f41c2 [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 Ohmichi0e836652015-01-08 04:38:56 +000019from tempest.common import service_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 Ohmichi0e836652015-01-08 04:38:56 +000042class BaremetalClient(service_client.ServiceClient):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030043 """
44 Base Tempest REST client for Ironic API.
45
46 """
47
Ken'ichi Ohmichi1f88ece2015-01-23 03:33:11 +000048 uri_prefix = ''
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030049
Ken'ichi Ohmichi19b6ff52015-01-23 02:45:50 +000050 def serialize(self, object_dict):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030051 """Serialize an Ironic object."""
52
Ken'ichi Ohmichi19b6ff52015-01-23 02:45:50 +000053 return json.dumps(object_dict)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030054
55 def deserialize(self, object_str):
56 """Deserialize an Ironic object."""
57
Ken'ichi Ohmichi19b6ff52015-01-23 02:45:50 +000058 return json.loads(object_str)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030059
60 def _get_uri(self, resource_name, uuid=None, permanent=False):
61 """
62 Get URI for a specific resource or object.
63
64 :param resource_name: The name of the REST resource, e.g., 'nodes'.
65 :param uuid: The unique identifier of an object in UUID format.
66 :return: Relative URI for the resource or object.
67
68 """
69 prefix = self.uri_prefix if not permanent else ''
70
71 return '{pref}/{res}{uuid}'.format(pref=prefix,
72 res=resource_name,
73 uuid='/%s' % uuid if uuid else '')
74
75 def _make_patch(self, allowed_attributes, **kw):
76 """
77 Create a JSON patch according to RFC 6902.
78
79 :param allowed_attributes: An iterable object that contains a set of
80 allowed attributes for an object.
81 :param **kw: Attributes and new values for them.
82 :return: A JSON path that sets values of the specified attributes to
83 the new ones.
84
85 """
86 def get_change(kw, path='/'):
87 for name, value in six.iteritems(kw):
88 if isinstance(value, dict):
89 for ch in get_change(value, path + '%s/' % name):
90 yield ch
91 else:
Adam Gandelman00682612014-09-02 17:10:36 -070092 if value is None:
93 yield {'path': path + name,
94 'op': 'remove'}
95 else:
96 yield {'path': path + name,
97 'value': value,
98 'op': 'replace'}
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030099
100 patch = [ch for ch in get_change(kw)
101 if ch['path'].lstrip('/') in allowed_attributes]
102
103 return patch
104
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400105 def _list_request(self, resource, permanent=False, **kwargs):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300106 """
107 Get the list of objects of the specified type.
108
109 :param resource: The name of the REST resource, e.g., 'nodes'.
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400110 "param **kw: Parameters for the request.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300111 :return: A tuple with the server response and deserialized JSON list
112 of objects
113
114 """
115 uri = self._get_uri(resource, permanent=permanent)
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400116 if kwargs:
117 uri += "?%s" % urllib.urlencode(kwargs)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300118
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200119 resp, body = self.get(uri)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000120 self.expected_success(200, resp['status'])
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300121
122 return resp, self.deserialize(body)
123
raiesmh08e5d84572014-06-23 09:49:03 +0530124 def _show_request(self, resource, uuid, permanent=False, **kwargs):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300125 """
126 Gets a specific object of the specified type.
127
128 :param uuid: Unique identifier of the object in UUID format.
129 :return: Serialized object as a dictionary.
130
131 """
raiesmh08e5d84572014-06-23 09:49:03 +0530132 if 'uri' in kwargs:
133 uri = kwargs['uri']
134 else:
135 uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200136 resp, body = self.get(uri)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000137 self.expected_success(200, resp['status'])
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300138
139 return resp, self.deserialize(body)
140
Ken'ichi Ohmichi19b6ff52015-01-23 02:45:50 +0000141 def _create_request(self, resource, object_dict):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300142 """
143 Create an object of the specified type.
144
145 :param resource: The name of the REST resource, e.g., 'nodes'.
146 :param object_dict: A Python dict that represents an object of the
147 specified type.
148 :return: A tuple with the server response and the deserialized created
149 object.
150
151 """
Ken'ichi Ohmichi19b6ff52015-01-23 02:45:50 +0000152 body = self.serialize(object_dict)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300153 uri = self._get_uri(resource)
154
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200155 resp, body = self.post(uri, body=body)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000156 self.expected_success(201, resp['status'])
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300157
158 return resp, self.deserialize(body)
159
160 def _delete_request(self, resource, uuid):
161 """
162 Delete specified object.
163
164 :param resource: The name of the REST resource, e.g., 'nodes'.
165 :param uuid: The unique identifier of an object in UUID format.
166 :return: A tuple with the server response and the response body.
167
168 """
169 uri = self._get_uri(resource, uuid)
170
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200171 resp, body = self.delete(uri)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000172 self.expected_success(204, resp['status'])
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300173 return resp, body
174
175 def _patch_request(self, resource, uuid, patch_object):
176 """
177 Update specified object with JSON-patch.
178
179 :param resource: The name of the REST resource, e.g., 'nodes'.
180 :param uuid: The unique identifier of an object in UUID format.
181 :return: A tuple with the server response and the serialized patched
182 object.
183
184 """
185 uri = self._get_uri(resource, uuid)
186 patch_body = json.dumps(patch_object)
187
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200188 resp, body = self.patch(uri, body=patch_body)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000189 self.expected_success(200, resp['status'])
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300190 return resp, self.deserialize(body)
191
192 @handle_errors
193 def get_api_description(self):
194 """Retrieves all versions of the Ironic API."""
195
196 return self._list_request('', permanent=True)
197
198 @handle_errors
199 def get_version_description(self, version='v1'):
200 """
201 Retrieves the desctription of the API.
202
203 :param version: The version of the API. Default: 'v1'.
204 :return: Serialized description of API resources.
205
206 """
207 return self._list_request(version, permanent=True)
Mh Raiesf8ecf232014-04-17 12:43:55 +0530208
209 def _put_request(self, resource, put_object):
210 """
211 Update specified object with JSON-patch.
212
213 """
214 uri = self._get_uri(resource)
215 put_body = json.dumps(put_object)
216
217 resp, body = self.put(uri, body=put_body)
Swapnil Kulkarniaa57d6e2014-08-19 10:40:35 +0000218 self.expected_success(202, resp['status'])
Mh Raiesf8ecf232014-04-17 12:43:55 +0530219 return resp, body