2544a7b6646a4921f31d1ecf4f98860e68868d63
[osm/osmclient.git] / osmclient / sol005 / role.py
1 # Copyright 2019 Whitestack, LLC
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 #
15 # For those usages not covered by the Apache License, Version 2.0 please
16 # contact: esousa@whitestack.com or glavado@whitestack.com
17 ##
18
19 """
20 OSM role mgmt API
21 """
22
23 from osmclient.common import utils
24 from osmclient.common.exceptions import ClientException
25 from osmclient.common.exceptions import NotFound
26 import json
27 import yaml
28 import logging
29
30
31 class Role(object):
32 def __init__(self, http=None, client=None):
33 self._http = http
34 self._client = client
35 self._logger = logging.getLogger('osmclient')
36 self._apiName = '/admin'
37 self._apiVersion = '/v1'
38 self._apiResource = '/roles'
39 self._apiBase = '{}{}{}'.format(self._apiName,
40 self._apiVersion, self._apiResource)
41
42 def create(self, name, permissions):
43 """
44 Creates a new OSM role.
45
46 :param name: name of the role.
47 :param permissions: permissions of the role in YAML.
48 :raises ClientException: when receives an unexpected from the server.
49 :raises ClientException: when fails creating a role.
50 """
51 self._logger.debug("")
52 self._client.get_token()
53 role = {"name": name}
54
55 if permissions:
56 role_permissions = yaml.safe_load(permissions)
57
58 if not isinstance(role_permissions, dict):
59 raise ClientException('Role permissions should be provided in a key-value fashion')
60
61 for key, value in role_permissions.items():
62 if not isinstance(value, bool):
63 raise ClientException("Value of '{}' in a role permissions should be boolean".format(key))
64
65 role["permissions"] = role_permissions
66
67 http_code, resp = self._http.post_cmd(endpoint=self._apiBase,
68 postfields_dict=role,
69 skip_query_admin=True)
70 # print('HTTP CODE: {}'.format(http_code))
71 # print('RESP: {}'.format(resp))
72 #if http_code in (200, 201, 202, 204):
73 if resp:
74 resp = json.loads(resp)
75 if not resp or 'id' not in resp:
76 raise ClientException('Unexpected response from server - {}'.format(
77 resp))
78 print(resp['id'])
79 #else:
80 # msg = ""
81 # if resp:
82 # try:
83 # msg = json.loads(resp)
84 # except ValueError:
85 # msg = resp
86 # raise ClientException("Failed to create role {} - {}".format(name, msg))
87
88 def update(self, name, new_name, permissions, add=None, remove=None):
89 """
90 Updates an OSM role identified by name.
91
92 NOTE: definition and add/remove are mutually exclusive.
93
94 :param name: name of the role
95 :param set_name: if provided, change the name.
96 :param permissions: if provided, overwrites the existing role specification. NOT IMPLEMENTED
97 :param add: if provided, adds new rules to the definition.
98 :param remove: if provided, removes rules from the definition.
99 :raises ClientException: when receives an unexpected response from the server.
100 :raises ClientException: when fails updating a role.
101 """
102 self._logger.debug("")
103 self._client.get_token()
104 if new_name is None and permissions is None and add is None and remove is None:
105 raise ClientException('At least one option should be provided')
106 elif permissions and (add or remove):
107 raise ClientException('permissions and add/remove are mutually exclusive')
108
109 role_obj = self.get(name)
110 new_role_obj = {"permissions": {}}
111 if new_name:
112 new_role_obj["name"] = new_name
113
114 if permissions:
115 role_definition = yaml.safe_load(permissions)
116
117 if not isinstance(role_definition, dict):
118 raise ClientException('Role permissions should be provided in a key-value fashion')
119
120 for key, value in role_definition.items():
121 if not isinstance(value, bool) and value is not None:
122 raise ClientException('Value in a role permissions should be boolean or None to remove')
123
124 new_role_obj["permissions"] = role_definition
125 else:
126 if remove:
127 keys_from_remove = yaml.safe_load(remove)
128
129 if not isinstance(keys_from_remove, list):
130 raise ClientException('Keys should be provided in a list fashion')
131
132 for key in keys_from_remove:
133 if not isinstance(key, str):
134 raise ClientException('Individual keys should be strings')
135 new_role_obj["permissions"][key] = None
136
137 if add:
138 add_roles = yaml.safe_load(add)
139
140 if not isinstance(add_roles, dict):
141 raise ClientException('Add should be provided in a key-value fashion')
142
143 for key, value in add_roles.items():
144 if not isinstance(value, bool):
145 raise ClientException("Value '{}' in a role permissions should be boolean".format(key))
146
147 new_role_obj["permissions"][key] = value
148 if not new_role_obj["permissions"]:
149 del new_role_obj["permissions"]
150
151 http_code, resp = self._http.patch_cmd(endpoint='{}/{}'.format(self._apiBase, role_obj['_id']),
152 postfields_dict=new_role_obj,
153 skip_query_admin=True)
154 # print('HTTP CODE: {}'.format(http_code))
155 # print('RESP: {}'.format(resp))
156 if http_code in (200, 201, 202):
157 if resp:
158 resp = json.loads(resp)
159 if not resp or 'id' not in resp:
160 raise ClientException('Unexpected response from server - {}'.format(
161 resp))
162 print(resp['id'])
163 elif http_code == 204:
164 print("Updated")
165 #else:
166 # msg = ""
167 # if resp:
168 # try:
169 # msg = json.loads(resp)
170 # except ValueError:
171 # msg = resp
172 # raise ClientException("Failed to update role {} - {}".format(name, msg))
173
174 def delete(self, name, force=False):
175 """
176 Deletes an OSM role identified by name.
177
178 :param name:
179 :param force:
180 :raises ClientException: when fails to delete a role.
181 """
182 self._logger.debug("")
183 self._client.get_token()
184 role = self.get(name)
185 querystring = ''
186 if force:
187 querystring = '?FORCE=True'
188 http_code, resp = self._http.delete_cmd('{}/{}{}'.format(self._apiBase,
189 role['_id'], querystring),
190 skip_query_admin=True)
191 # print('HTTP CODE: {}'.format(http_code))
192 # print('RESP: {}'.format(resp))
193 if http_code == 202:
194 print('Deletion in progress')
195 elif http_code == 204:
196 print('Deleted')
197 elif resp and 'result' in resp:
198 print('Deleted')
199 else:
200 msg = resp or ""
201 # if resp:
202 # try:
203 # msg = json.loads(resp)
204 # except ValueError:
205 # msg = resp
206 raise ClientException("Failed to delete role {} - {}".format(name, msg))
207
208 def list(self, filter=None):
209 """
210 Returns the list of OSM role.
211
212 :param filter:
213 :returns:
214 """
215 self._logger.debug("")
216 self._client.get_token()
217 filter_string = ''
218 if filter:
219 filter_string = '?{}'.format(filter)
220 _, resp = self._http.get2_cmd('{}{}'.format(self._apiBase, filter_string),skip_query_admin=True)
221 # print('RESP: {}'.format(resp))
222 if resp:
223 return json.loads(resp)
224 return list()
225
226 def get(self, name):
227 """
228 Returns a specific OSM role based on name or id.
229
230 :param name:
231 :raises NotFound: when the role is not found.
232 :returns: the specified role.
233 """
234 self._logger.debug("")
235 self._client.get_token()
236 if utils.validate_uuid4(name):
237 for role in self.list():
238 if name == role['_id']:
239 return role
240 else:
241 for role in self.list():
242 if name == role['name']:
243 return role
244 raise NotFound("Role {} not found".format(name))
245