blob: 3d4fa501be383b418c5c89f419c5edbd0f884c2a [file] [log] [blame]
Roman Prykhodchenko62b1ed12013-10-16 21:51:47 +03001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15import functools
16import json
17
18import six
19
20from tempest.common import rest_client
21
22
23def handle_errors(f):
24 """A decorator that allows to ignore certain types of errors."""
25
26 @functools.wraps(f)
27 def wrapper(*args, **kwargs):
28 param_name = 'ignore_errors'
29 ignored_errors = kwargs.get(param_name, tuple())
30
31 if param_name in kwargs:
32 del kwargs[param_name]
33
34 try:
35 return f(*args, **kwargs)
36 except ignored_errors:
37 # Silently ignore errors
38 pass
39
40 return wrapper
41
42
43class BaremetalClient(rest_client.RestClient):
44 """
45 Base Tempest REST client for Ironic API.
46
47 """
48
49 def __init__(self, config, username, password, auth_url, tenant_name=None):
50 super(BaremetalClient, self).__init__(config, username, password,
51 auth_url, tenant_name)
52 self.service = self.config.baremetal.catalog_type
53 self.uri_prefix = ''
54
55 def serialize(self, object_type, object_dict):
56 """Serialize an Ironic object."""
57
58 raise NotImplementedError
59
60 def deserialize(self, object_str):
61 """Deserialize an Ironic object."""
62
63 raise NotImplementedError
64
65 def _get_uri(self, resource_name, uuid=None, permanent=False):
66 """
67 Get URI for a specific resource or object.
68
69 :param resource_name: The name of the REST resource, e.g., 'nodes'.
70 :param uuid: The unique identifier of an object in UUID format.
71 :return: Relative URI for the resource or object.
72
73 """
74 prefix = self.uri_prefix if not permanent else ''
75
76 return '{pref}/{res}{uuid}'.format(pref=prefix,
77 res=resource_name,
78 uuid='/%s' % uuid if uuid else '')
79
80 def _make_patch(self, allowed_attributes, **kw):
81 """
82 Create a JSON patch according to RFC 6902.
83
84 :param allowed_attributes: An iterable object that contains a set of
85 allowed attributes for an object.
86 :param **kw: Attributes and new values for them.
87 :return: A JSON path that sets values of the specified attributes to
88 the new ones.
89
90 """
91 def get_change(kw, path='/'):
92 for name, value in six.iteritems(kw):
93 if isinstance(value, dict):
94 for ch in get_change(value, path + '%s/' % name):
95 yield ch
96 else:
97 yield {'path': path + name,
98 'value': value,
99 'op': 'replace'}
100
101 patch = [ch for ch in get_change(kw)
102 if ch['path'].lstrip('/') in allowed_attributes]
103
104 return patch
105
106 def _list_request(self, resource, permanent=False):
107 """
108 Get the list of objects of the specified type.
109
110 :param resource: The name of the REST resource, e.g., 'nodes'.
111 :return: A tuple with the server response and deserialized JSON list
112 of objects
113
114 """
115 uri = self._get_uri(resource, permanent=permanent)
116
117 resp, body = self.get(uri, self.headers)
118
119 return resp, self.deserialize(body)
120
121 def _show_request(self, resource, uuid, permanent=False):
122 """
123 Gets a specific object of the specified type.
124
125 :param uuid: Unique identifier of the object in UUID format.
126 :return: Serialized object as a dictionary.
127
128 """
129 uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
130 resp, body = self.get(uri, self.headers)
131
132 return resp, self.deserialize(body)
133
134 def _create_request(self, resource, object_type, object_dict):
135 """
136 Create an object of the specified type.
137
138 :param resource: The name of the REST resource, e.g., 'nodes'.
139 :param object_dict: A Python dict that represents an object of the
140 specified type.
141 :return: A tuple with the server response and the deserialized created
142 object.
143
144 """
145 body = self.serialize(object_type, object_dict)
146 uri = self._get_uri(resource)
147
148 resp, body = self.post(uri, headers=self.headers, body=body)
149
150 return resp, self.deserialize(body)
151
152 def _delete_request(self, resource, uuid):
153 """
154 Delete specified object.
155
156 :param resource: The name of the REST resource, e.g., 'nodes'.
157 :param uuid: The unique identifier of an object in UUID format.
158 :return: A tuple with the server response and the response body.
159
160 """
161 uri = self._get_uri(resource, uuid)
162
163 resp, body = self.delete(uri, self.headers)
164 return resp, body
165
166 def _patch_request(self, resource, uuid, patch_object):
167 """
168 Update specified object with JSON-patch.
169
170 :param resource: The name of the REST resource, e.g., 'nodes'.
171 :param uuid: The unique identifier of an object in UUID format.
172 :return: A tuple with the server response and the serialized patched
173 object.
174
175 """
176 uri = self._get_uri(resource, uuid)
177 patch_body = json.dumps(patch_object)
178
179 resp, body = self.patch(uri, headers=self.headers, body=patch_body)
180 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'):
190 """
191 Retrieves the desctription of the API.
192
193 :param version: The version of the API. Default: 'v1'.
194 :return: Serialized description of API resources.
195
196 """
197 return self._list_request(version, permanent=True)