-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathmeraki_admins.py
213 lines (168 loc) · 7.67 KB
/
meraki_admins.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import requests
BASE_URL = "https://dashboard.meraki.com/api/v0"
API_HEADER = "X-Cisco-Meraki-API-Key"
JSON_KEY = "Content-Type"
JSON_VAL = "application/json"
def get_data(headers, level="", request_string="", url_id="", ext_url=""):
""" Defines a base get request to the Meraki Dashboard.
One can be built using this function, or a pre-formatted one can be
passed in.
Args:
level: A string of which level of data to query from; current valid
top-level request types are organizations or networks.
request_string: String indicating type of data to be queried; some
requests such as determining Org access for a given API key do
not require one.
url_id: String containing an Org or Network ID.
ext_url: an externally formatted URL; supersedes all other
parameters if specified
Returns:
A requests.get object containing, among other things, the HTTP
HTTP return code of the request, and the returned data.
"""
# TODO (Alex): Docstring needs some work here to build-out what different
# request strings indicate, and will need to work on adding exceptions.
if ext_url:
data = requests.get(ext_url, headers=headers)
else:
url = "%s/%s/%s/%s/" % (BASE_URL.strip("/"), level.strip("/"),
url_id.strip("/"), request_string.strip("/"))
data = requests.get(url, headers=headers)
return data
class Error(Exception):
"""Base module exception."""
pass
class InvalidOrgPermissions(Error):
"""Thrown when invalid Org-level permissions are supplied."""
def __init__(self, provided=None, valid=None):
self.provided = provided
self.valid = valid
self.default = "Org Permissions must be FULL, READ-ONLY, or NONE"
def __str__(self):
return repr(self.default)
class InvalidNetTagPermissions(Error):
"""Thrown when invalid Network or Tag permissions are supplied."""
pass
class NullPermissionError(Error):
"""Thrown when no permissionsare supplied."""
def __init__(self):
self.default = "No Org or Tag/Network level permissions supplied."
def __str__(self):
return repr(self.default)
class FormatError(Error):
"""Thrown when imprperly formatted data received."""
pass
class DashboardAdmins(object):
""" All methods and handlers to define, modify, or remove an
admin from a given Dashboard account.
"""
def __init__(self, org_id, api_key):
self.url = "%s/organizations/%s/admins" % (BASE_URL, org_id)
self.valid_tag_keys = {"tag", "access"}
self.valid_access_vals = {"full", "read-only", "none"}
self.valid_target_vals = self.valid_access_vals.union({"monitor-only",
"guest-ambassador"})
self.headers = {API_HEADER: api_key}
def __provided_access_valid(self, access):
if (access not in self.valid_access_vals and
access not in self.valid_target_vals):
raise InvalidOrgPermissions(access, self.valid_access_vals)
def __provided_tags_valid(self, tags):
if not isinstance(tags, list):
raise TypeError("Tags must be provided as a list of dictionaries.")
for i in tags:
if not isinstance(i, dict):
raise TypeError("Tags must be provided as \
a list of dictionaries.")
elif not self.valid_tag_keys.issuperset(i.keys()):
raise FormatError("Error in tag format.")
self.__provided_access_valid(i["access"])
def __admin_exists(self, admin_id):
check = get_data(ext_url=self.url, headers=self.headers)
try:
for admin in check.json():
if admin["email"] == admin_id or admin["id"] == admin_id:
return admin
except ValueError: # Ignore empty responses
pass
return None
def add_admin(self, name, email, orgAccess, networks=None, tags=None):
""" Define a new org-level Admin account on Dashboard under
Organization -> Administrators.
Args:
name: Name of the new admin.
email: Email of the new admin.
orgAccess: Their access level; valid values are full,
read-only, or none (for tag or network-level admins)
networks: A list of dictionaries formatted as
[{id: network-id, access: access-level}]; networks must be
prexisting on Dashboard.
tags: A list of dictionaries formatted as
[{tag:tag-name}, {access:access-level}]; tags don't need to be
prexisting on Dashboard.
Returns:
new_admin: a request object of the new admin's values
as specified by the passed arguments and the HTTP
return code for it, or None if the user already exists
"""
admin = {"name": name, "email": email, "orgAccess": orgAccess}
self.__provided_access_valid(orgAccess)
if orgAccess.lower() == "none" and not tags and not networks:
raise NullPermissionError()
if tags:
self.__provided_tags_valid(tags)
admin["tags"] = tags
if networks:
admin["networks"] = networks # still deciding how this is going to
# be validated
new_admin = requests.post(self.url, json=admin, headers=self.headers)
return new_admin
def update_admin(self, admin_id, name=None, orgAccess=None, networks=None,
tags=None):
"""Update an existing admin's permissions or access.
Dashboard ignores null or None values in spite of what it returns, and
will not modify anything based on null parameters.
Args:
admin_id: A user ID string or email address.
to_update: a dict of the fields to be updated; valid keys are
orgAccess, name, tags, and network.
Returns:
updated: The request object of the updated admin, or None if the
passed admin ID doesn't exist.
"""
exists = self.__admin_exists(admin_id)
if not exists:
return None
elif not admin_id.isdigit():
admin_id = exists["id"]
to_update = {"id": admin_id, "orgAccess": orgAccess, "name": name}
update_url = self.url+"/"+admin_id
if tags:
self.__provided_tags_valid(tags)
to_update["tags"] = tags
if orgAccess:
try:
self.__provided_access_valid(orgAccess)
except InvalidOrgPermissions as err:
print "ERROR: Invalid permissions for user %s \nPROVIDED: %s \
\nEXPECTED: %s" % (admin_id, err.provided, err.valid)
if networks:
to_update["networks"] = networks
updated = requests.put(url=update_url, json=to_update,
headers=self.headers)
return updated
def del_admin(self, admin_id):
""" Delete a specified admin account.
Args:
admin_id: ID string or email of the admin to be deleted.
Returns:
deleted: The request object of the deleted admin, or None if the
passed admin ID doesn't exist.
"""
exists = self.__admin_exists(admin_id)
if not exists:
return None
elif not admin_id.isdigit():
admin_id = exists["id"]
url = self.url+"/"+admin_id
return requests.delete(url, headers=self.headers)