blob: 5b6dea8fb90ab7307b3d431c3983c3dd4bd87e26 [file] [log] [blame]
Yuiko Takadab6527002015-12-07 11:49:12 +09001# 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
14
15from oslo_serialization import jsonutils as json
16import six
17from six.moves.urllib import parse as urllib
Lenny Verkhovsky88625042016-03-08 17:44:00 +020018from tempest.lib.common import rest_client
Yuiko Takadab6527002015-12-07 11:49:12 +090019
20
21def handle_errors(f):
22 """A decorator that allows to ignore certain types of errors."""
23
24 @functools.wraps(f)
25 def wrapper(*args, **kwargs):
26 param_name = 'ignore_errors'
27 ignored_errors = kwargs.get(param_name, tuple())
28
29 if param_name in kwargs:
30 del kwargs[param_name]
31
32 try:
33 return f(*args, **kwargs)
34 except ignored_errors:
35 # Silently ignore errors
36 pass
37
38 return wrapper
39
40
41class BaremetalClient(rest_client.RestClient):
42 """Base Tempest REST client for Ironic API."""
43
44 uri_prefix = ''
45
46 def serialize(self, object_dict):
47 """Serialize an Ironic object."""
48
49 return json.dumps(object_dict)
50
51 def deserialize(self, object_str):
52 """Deserialize an Ironic object."""
53
54 return json.loads(object_str)
55
56 def _get_uri(self, resource_name, uuid=None, permanent=False):
57 """Get URI for a specific resource or object.
58
59 :param resource_name: The name of the REST resource, e.g., 'nodes'.
60 :param uuid: The unique identifier of an object in UUID format.
61 :returns: Relative URI for the resource or object.
62
63 """
64 prefix = self.uri_prefix if not permanent else ''
65
66 return '{pref}/{res}{uuid}'.format(pref=prefix,
67 res=resource_name,
68 uuid='/%s' % uuid if uuid else '')
69
70 def _make_patch(self, allowed_attributes, **kwargs):
71 """Create a JSON patch according to RFC 6902.
72
73 :param allowed_attributes: An iterable object that contains a set of
74 allowed attributes for an object.
75 :param **kwargs: Attributes and new values for them.
76 :returns: A JSON path that sets values of the specified attributes to
77 the new ones.
78
79 """
80 def get_change(kwargs, path='/'):
81 for name, value in six.iteritems(kwargs):
82 if isinstance(value, dict):
83 for ch in get_change(value, path + '%s/' % name):
84 yield ch
85 else:
86 if value is None:
87 yield {'path': path + name,
88 'op': 'remove'}
89 else:
90 yield {'path': path + name,
91 'value': value,
92 'op': 'replace'}
93
94 patch = [ch for ch in get_change(kwargs)
95 if ch['path'].lstrip('/') in allowed_attributes]
96
97 return patch
98
99 def _list_request(self, resource, permanent=False, **kwargs):
100 """Get the list of objects of the specified type.
101
102 :param resource: The name of the REST resource, e.g., 'nodes'.
103 :param **kwargs: Parameters for the request.
104 :returns: A tuple with the server response and deserialized JSON list
105 of objects
106
107 """
108 uri = self._get_uri(resource, permanent=permanent)
109 if kwargs:
110 uri += "?%s" % urllib.urlencode(kwargs)
111
112 resp, body = self.get(uri)
113 self.expected_success(200, resp['status'])
114
115 return resp, self.deserialize(body)
116
117 def _show_request(self, resource, uuid, permanent=False, **kwargs):
118 """Gets a specific object of the specified type.
119
120 :param uuid: Unique identifier of the object in UUID format.
121 :returns: Serialized object as a dictionary.
122
123 """
124 if 'uri' in kwargs:
125 uri = kwargs['uri']
126 else:
127 uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
128 resp, body = self.get(uri)
129 self.expected_success(200, resp['status'])
130
131 return resp, self.deserialize(body)
132
133 def _create_request(self, resource, object_dict):
134 """Create an object of the specified type.
135
136 :param resource: The name of the REST resource, e.g., 'nodes'.
137 :param object_dict: A Python dict that represents an object of the
138 specified type.
139 :returns: A tuple with the server response and the deserialized created
140 object.
141
142 """
143 body = self.serialize(object_dict)
144 uri = self._get_uri(resource)
145
146 resp, body = self.post(uri, body=body)
147 self.expected_success(201, resp['status'])
148
149 return resp, self.deserialize(body)
150
151 def _delete_request(self, resource, uuid):
152 """Delete specified object.
153
154 :param resource: The name of the REST resource, e.g., 'nodes'.
155 :param uuid: The unique identifier of an object in UUID format.
156 :returns: A tuple with the server response and the response body.
157
158 """
159 uri = self._get_uri(resource, uuid)
160
161 resp, body = self.delete(uri)
162 self.expected_success(204, resp['status'])
163 return resp, body
164
165 def _patch_request(self, resource, uuid, patch_object):
166 """Update specified object with JSON-patch.
167
168 :param resource: The name of the REST resource, e.g., 'nodes'.
169 :param uuid: The unique identifier of an object in UUID format.
170 :returns: A tuple with the server response and the serialized patched
171 object.
172
173 """
174 uri = self._get_uri(resource, uuid)
175 patch_body = json.dumps(patch_object)
176
177 resp, body = self.patch(uri, body=patch_body)
178 self.expected_success(200, resp['status'])
179 return resp, self.deserialize(body)
180
181 @handle_errors
182 def get_api_description(self):
183 """Retrieves all versions of the Ironic API."""
184
185 return self._list_request('', permanent=True)
186
187 @handle_errors
188 def get_version_description(self, version='v1'):
189 """Retrieves the desctription of the API.
190
191 :param version: The version of the API. Default: 'v1'.
192 :returns: Serialized description of API resources.
193
194 """
195 return self._list_request(version, permanent=True)
196
197 def _put_request(self, resource, put_object):
198 """Update specified object with JSON-patch."""
199 uri = self._get_uri(resource)
200 put_body = json.dumps(put_object)
201
202 resp, body = self.put(uri, body=put_body)
203 self.expected_success(202, resp['status'])
204 return resp, body