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