c80e50e5954e9c664849a0d67561b2d6f5ebbddb
[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 # print('HTTP CODE: {}'.format(http_code))
70 # print('RESP: {}'.format(resp))
71 #if http_code in (200, 201, 202, 204):
72 if resp:
73 resp = json.loads(resp)
74 if not resp or 'id' not in resp:
75 raise ClientException('Unexpected response from server - {}'.format(
76 resp))
77 print(resp['id'])
78 #else:
79 # msg = ""
80 # if resp:
81 # try:
82 # msg = json.loads(resp)
83 # except ValueError:
84 # msg = resp
85 # raise ClientException("Failed to create role {} - {}".format(name, msg))
86
87 def update(self, name, new_name, permissions, add=None, remove=None):
88 """
89 Updates an OSM role identified by name.
90
91 NOTE: definition and add/remove are mutually exclusive.
92
93 :param name: name of the role
94 :param set_name: if provided, change the name.
95 :param permissions: if provided, overwrites the existing role specification. NOT IMPLEMENTED
96 :param add: if provided, adds new rules to the definition.
97 :param remove: if provided, removes rules from the definition.
98 :raises ClientException: when receives an unexpected response from the server.
99 :raises ClientException: when fails updating a role.
100 """
101 self._logger.debug("")
102 self._client.get_token()
103 if new_name is None and permissions is None and add is None and remove is None:
104 raise ClientException('At least one option should be provided')
105 elif permissions and (add or remove):
106 raise ClientException('permissions and add/remove are mutually exclusive')
107
108 role_obj = self.get(name)
109 new_role_obj = {"permissions": {}}
110 if new_name:
111 new_role_obj["name"] = new_name
112
113 if permissions:
114 role_definition = yaml.safe_load(permissions)
115
116 if not isinstance(role_definition, dict):
117 raise ClientException('Role permissions should be provided in a key-value fashion')
118
119 for key, value in role_definition.items():
120 if not isinstance(value, bool) and value is not None:
121 raise ClientException('Value in a role permissions should be boolean or None to remove')
122
123 new_role_obj["permissions"] = role_definition
124 else:
125 if remove:
126 keys_from_remove = yaml.safe_load(remove)
127
128 if not isinstance(keys_from_remove, list):
129 raise ClientException('Keys should be provided in a list fashion')
130
131 for key in keys_from_remove:
132 if not isinstance(key, str):
133 raise ClientException('Individual keys should be strings')
134 new_role_obj["permissions"][key] = None
135
136 if add:
137 add_roles = yaml.safe_load(add)
138
139 if not isinstance(add_roles, dict):
140 raise ClientException('Add should be provided in a key-value fashion')
141
142 for key, value in add_roles.items():
143 if not isinstance(value, bool):
144 raise ClientException("Value '{}' in a role permissions should be boolean".format(key))
145
146 new_role_obj["permissions"][key] = value
147 if not new_role_obj["permissions"]:
148 del new_role_obj["permissions"]
149
150 http_code, resp = self._http.patch_cmd(endpoint='{}/{}'.format(self._apiBase, role_obj['_id']),
151 postfields_dict=new_role_obj)
152 # print('HTTP CODE: {}'.format(http_code))
153 # print('RESP: {}'.format(resp))
154 if http_code in (200, 201, 202):
155 if resp:
156 resp = json.loads(resp)
157 if not resp or 'id' not in resp:
158 raise ClientException('Unexpected response from server - {}'.format(
159 resp))
160 print(resp['id'])
161 elif http_code == 204:
162 print("Updated")
163 #else:
164 # msg = ""
165 # if resp:
166 # try:
167 # msg = json.loads(resp)
168 # except ValueError:
169 # msg = resp
170 # raise ClientException("Failed to update role {} - {}".format(name, msg))
171
172 def delete(self, name, force=False):
173 """
174 Deletes an OSM role identified by name.
175
176 :param name:
177 :param force:
178 :raises ClientException: when fails to delete a role.
179 """
180 self._logger.debug("")
181 self._client.get_token()
182 role = self.get(name)
183 querystring = ''
184 if force:
185 querystring = '?FORCE=True'
186 http_code, resp = self._http.delete_cmd('{}/{}{}'.format(self._apiBase,
187 role['_id'], querystring))
188 # print('HTTP CODE: {}'.format(http_code))
189 # print('RESP: {}'.format(resp))
190 if http_code == 202:
191 print('Deletion in progress')
192 elif http_code == 204:
193 print('Deleted')
194 elif resp and 'result' in resp:
195 print('Deleted')
196 else:
197 msg = resp or ""
198 # if resp:
199 # try:
200 # msg = json.loads(resp)
201 # except ValueError:
202 # msg = resp
203 raise ClientException("Failed to delete role {} - {}".format(name, msg))
204
205 def list(self, filter=None):
206 """
207 Returns the list of OSM role.
208
209 :param filter:
210 :returns:
211 """
212 self._logger.debug("")
213 self._client.get_token()
214 filter_string = ''
215 if filter:
216 filter_string = '?{}'.format(filter)
217 _, resp = self._http.get2_cmd('{}{}'.format(self._apiBase, filter_string))
218 # print('RESP: {}'.format(resp))
219 if resp:
220 return json.loads(resp)
221 return list()
222
223 def get(self, name):
224 """
225 Returns a specific OSM role based on name or id.
226
227 :param name:
228 :raises NotFound: when the role is not found.
229 :returns: the specified role.
230 """
231 self._logger.debug("")
232 self._client.get_token()
233 if utils.validate_uuid4(name):
234 for role in self.list():
235 if name == role['_id']:
236 return role
237 else:
238 for role in self.list():
239 if name == role['name']:
240 return role
241 raise NotFound("Role {} not found".format(name))
242