blob: f98ecff56cf8dcac99b601d9542390d29cf4ee50 [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
14import json
Sergey Nikitin0d43eb52014-02-03 14:50:02 +040015import urllib
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030016
17import six
18
19from tempest.common import rest_client
Matthew Treinish684d8992014-01-30 16:27:40 +000020from tempest import config
21
22CONF = config.CONF
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030023
24
25def handle_errors(f):
26 """A decorator that allows to ignore certain types of errors."""
27
28 @functools.wraps(f)
29 def wrapper(*args, **kwargs):
30 param_name = 'ignore_errors'
31 ignored_errors = kwargs.get(param_name, tuple())
32
33 if param_name in kwargs:
34 del kwargs[param_name]
35
36 try:
37 return f(*args, **kwargs)
38 except ignored_errors:
39 # Silently ignore errors
40 pass
41
42 return wrapper
43
44
45class BaremetalClient(rest_client.RestClient):
46 """
47 Base Tempest REST client for Ironic API.
48
49 """
50
Andrea Frittoli8bbdb162014-01-06 11:06:13 +000051 def __init__(self, auth_provider):
52 super(BaremetalClient, self).__init__(auth_provider)
Matthew Treinish684d8992014-01-30 16:27:40 +000053 self.service = CONF.baremetal.catalog_type
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +030054 self.uri_prefix = ''
55
56 def serialize(self, object_type, object_dict):
57 """Serialize an Ironic object."""
58
59 raise NotImplementedError
60
61 def deserialize(self, object_str):
62 """Deserialize an Ironic object."""
63
64 raise NotImplementedError
65
66 def _get_uri(self, resource_name, uuid=None, permanent=False):
67 """
68 Get URI for a specific resource or object.
69
70 :param resource_name: The name of the REST resource, e.g., 'nodes'.
71 :param uuid: The unique identifier of an object in UUID format.
72 :return: Relative URI for the resource or object.
73
74 """
75 prefix = self.uri_prefix if not permanent else ''
76
77 return '{pref}/{res}{uuid}'.format(pref=prefix,
78 res=resource_name,
79 uuid='/%s' % uuid if uuid else '')
80
81 def _make_patch(self, allowed_attributes, **kw):
82 """
83 Create a JSON patch according to RFC 6902.
84
85 :param allowed_attributes: An iterable object that contains a set of
86 allowed attributes for an object.
87 :param **kw: Attributes and new values for them.
88 :return: A JSON path that sets values of the specified attributes to
89 the new ones.
90
91 """
92 def get_change(kw, path='/'):
93 for name, value in six.iteritems(kw):
94 if isinstance(value, dict):
95 for ch in get_change(value, path + '%s/' % name):
96 yield ch
97 else:
98 yield {'path': path + name,
99 'value': value,
100 'op': 'replace'}
101
102 patch = [ch for ch in get_change(kw)
103 if ch['path'].lstrip('/') in allowed_attributes]
104
105 return patch
106
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400107 def _list_request(self, resource, permanent=False, **kwargs):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300108 """
109 Get the list of objects of the specified type.
110
111 :param resource: The name of the REST resource, e.g., 'nodes'.
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400112 "param **kw: Parameters for the request.
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300113 :return: A tuple with the server response and deserialized JSON list
114 of objects
115
116 """
117 uri = self._get_uri(resource, permanent=permanent)
Sergey Nikitin0d43eb52014-02-03 14:50:02 +0400118 if kwargs:
119 uri += "?%s" % urllib.urlencode(kwargs)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300120
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200121 resp, body = self.get(uri)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300122
123 return resp, self.deserialize(body)
124
raiesmh08e5d84572014-06-23 09:49:03 +0530125 def _show_request(self, resource, uuid, permanent=False, **kwargs):
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300126 """
127 Gets a specific object of the specified type.
128
129 :param uuid: Unique identifier of the object in UUID format.
130 :return: Serialized object as a dictionary.
131
132 """
raiesmh08e5d84572014-06-23 09:49:03 +0530133 if 'uri' in kwargs:
134 uri = kwargs['uri']
135 else:
136 uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200137 resp, body = self.get(uri)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300138
139 return resp, self.deserialize(body)
140
141 def _create_request(self, resource, object_type, object_dict):
142 """
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 """
152 body = self.serialize(object_type, object_dict)
153 uri = self._get_uri(resource)
154
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200155 resp, body = self.post(uri, body=body)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300156
157 return resp, self.deserialize(body)
158
159 def _delete_request(self, resource, uuid):
160 """
161 Delete specified object.
162
163 :param resource: The name of the REST resource, e.g., 'nodes'.
164 :param uuid: The unique identifier of an object in UUID format.
165 :return: A tuple with the server response and the response body.
166
167 """
168 uri = self._get_uri(resource, uuid)
169
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200170 resp, body = self.delete(uri)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300171 return resp, body
172
173 def _patch_request(self, resource, uuid, patch_object):
174 """
175 Update specified object with JSON-patch.
176
177 :param resource: The name of the REST resource, e.g., 'nodes'.
178 :param uuid: The unique identifier of an object in UUID format.
179 :return: A tuple with the server response and the serialized patched
180 object.
181
182 """
183 uri = self._get_uri(resource, uuid)
184 patch_body = json.dumps(patch_object)
185
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200186 resp, body = self.patch(uri, body=patch_body)
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +0300187 return resp, self.deserialize(body)
188
189 @handle_errors
190 def get_api_description(self):
191 """Retrieves all versions of the Ironic API."""
192
193 return self._list_request('', permanent=True)
194
195 @handle_errors
196 def get_version_description(self, version='v1'):
197 """
198 Retrieves the desctription of the API.
199
200 :param version: The version of the API. Default: 'v1'.
201 :return: Serialized description of API resources.
202
203 """
204 return self._list_request(version, permanent=True)
Mh Raiesf8ecf232014-04-17 12:43:55 +0530205
206 def _put_request(self, resource, put_object):
207 """
208 Update specified object with JSON-patch.
209
210 """
211 uri = self._get_uri(resource)
212 put_body = json.dumps(put_object)
213
214 resp, body = self.put(uri, body=put_body)
215 return resp, body