733e00f3a67ac1e7acb3ef6f1c38d3e7e8a1b577
[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 from osmclient.common.exceptions import OsmHttpException
27 import json
28 import yaml
29 import logging
30
31
32 class Role(object):
33 def __init__(self, http=None, client=None):
34 self._http = http
35 self._client = client
36 self._logger = logging.getLogger('osmclient')
37 self._apiName = '/admin'
38 self._apiVersion = '/v1'
39 self._apiResource = '/roles'
40 self._apiBase = '{}{}{}'.format(self._apiName,
41 self._apiVersion, self._apiResource)
42
43 def create(self, name, permissions):
44 """
45 Creates a new OSM role.
46
47 :param name: name of the role.
48 :param permissions: permissions of the role in YAML.
49 :raises ClientException: when receives an unexpected from the server.
50 :raises ClientException: when fails creating a role.
51 """
52 self._logger.debug("")
53 self._client.get_token()
54 role = {"name": name}
55
56 if permissions:
57 role_permissions = yaml.safe_load(permissions)
58
59 if not isinstance(role_permissions, dict):
60 raise ClientException('Role permissions should be provided in a key-value fashion')
61
62 for key, value in role_permissions.items():
63 if not isinstance(value, bool):
64 raise ClientException("Value of '{}' in a role permissions should be boolean".format(key))
65
66 role["permissions"] = role_permissions
67
68 http_code, resp = self._http.post_cmd(endpoint=self._apiBase,
69 postfields_dict=role)
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 OsmHttpException('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 # print('HTTP CODE: {}'.format(http_code))
154 # print('RESP: {}'.format(resp))
155 if http_code in (200, 201, 202):
156 if resp:
157 resp = json.loads(resp)
158 if not resp or 'id' not in resp:
159 raise OsmHttpException('Unexpected response from server - {}'.format(
160 resp))
161 print(resp['id'])
162 elif http_code == 204:
163 print("Updated")
164 #else:
165 # msg = ""
166 # if resp:
167 # try:
168 # msg = json.loads(resp)
169 # except ValueError:
170 # msg = resp
171 # raise ClientException("Failed to update role {} - {}".format(name, msg))
172
173 def delete(self, name, force=False):
174 """
175 Deletes an OSM role identified by name.
176
177 :param name:
178 :param force:
179 :raises ClientException: when fails to delete a role.
180 """
181 self._logger.debug("")
182 self._client.get_token()
183 role = self.get(name)
184 querystring = ''
185 if force:
186 querystring = '?FORCE=True'
187 http_code, resp = self._http.delete_cmd('{}/{}{}'.format(self._apiBase,
188 role['_id'], querystring))
189 # print('HTTP CODE: {}'.format(http_code))
190 # print('RESP: {}'.format(resp))
191 if http_code == 202:
192 print('Deletion in progress')
193 elif http_code == 204:
194 print('Deleted')
195 elif resp and 'result' in resp:
196 print('Deleted')
197 else:
198 msg = ""
199 if resp:
200 try:
201 msg = json.loads(resp)
202 except ValueError:
203 msg = resp
204 raise OsmHttpException("Failed to delete role {} - {}".format(name, msg))
205
206 def list(self, filter=None):
207 """
208 Returns the list of OSM role.
209
210 :param filter:
211 :returns:
212 """
213 self._logger.debug("")
214 self._client.get_token()
215 filter_string = ''
216 if filter:
217 filter_string = '?{}'.format(filter)
218 _, resp = self._http.get2_cmd('{}{}'.format(self._apiBase, filter_string))
219 # print('RESP: {}'.format(resp))
220 if resp:
221 return json.loads(resp)
222 return list()
223
224 def get(self, name):
225 """
226 Returns a specific OSM role based on name or id.
227
228 :param name:
229 :raises NotFound: when the role is not found.
230 :returns: the specified role.
231 """
232 self._logger.debug("")
233 self._client.get_token()
234 if utils.validate_uuid4(name):
235 for role in self.list():
236 if name == role['_id']:
237 return role
238 else:
239 for role in self.list():
240 if name == role['name']:
241 return role
242 raise NotFound("Role {} not found".format(name))
243