/[marcuscom]/meraki/setup_meraki_nets.py
ViewVC logotype

Contents of /meraki/setup_meraki_nets.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 20640 - (show annotations) (download) (as text)
Sun Jul 28 18:14:26 2019 UTC (7 weeks, 6 days ago) by jclarke
File MIME type: text/x-python
File size: 17648 byte(s)
Fix a number of typos and bugs.

Notably, update for recent API changes.

1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2018 Joe Clarke <jclarke@cisco.com>
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 # 1. Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # 2. Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 #
14 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 # SUCH DAMAGE.
25
26
27 import meraki_api
28 import yaml
29 import argparse
30 import sys
31 import os
32 from colorama import Fore, Back, Style
33 import colorama
34
35 BANNER = '[{}] **********************************************************'
36
37
38 def main():
39 parser = argparse.ArgumentParser(
40 prog=sys.argv[0], description='Add devices to network')
41 parser.add_argument('--config', '-c', metavar='<CONFIG FILE>',
42 help='Path to the organization configuration file', required=True)
43 parser.add_argument(
44 '--networks', '-n', metavar='<NETWORK>[,<NETWORK>[,...]]', help='Comma-separated list of networks to process')
45 args = parser.parse_args()
46
47 colorama.init()
48
49 if not os.path.isfile(args.config):
50 print('Config file {} does not exist or is not a file!'.format(args.config))
51 sys.exit(1)
52
53 print(BANNER.format('Loading config file'))
54 with open(args.config, 'r') as c:
55 config = yaml.load(c)
56 print('{}ok{}\n'.format(Fore.GREEN, Style.RESET_ALL))
57
58 for key in ['api_key', 'organization', 'networks']:
59 if not key in config:
60 print('Invalid config: {} is missing!'.format(key))
61 sys.exit(1)
62
63 meraki = meraki_api.Meraki(key=config['api_key'])
64 orgs = meraki.get_organizations()
65 org = None
66 for o in orgs:
67 if o.get('name') == config['organization']:
68 org = o
69 break
70
71 if org is None:
72 print('Failed to find organization {} in this profile!'.format(
73 config['organization']))
74 sys.exit(1)
75
76 nets = org.get_networks()
77 inv = org.get_inventory()
78
79 errors = 0
80
81 configure_nets = None
82 if args.networks is not None:
83 configure_nets = args.networks.split(',')
84
85 for net in config['networks']:
86 nerrors = 0
87 net_obj = None
88 nname = net.keys()[0]
89 print(BANNER.format('Configuring network {}'.format(nname)))
90 if configure_nets is not None and nname not in configure_nets:
91 print('{}skipping (not in specified network list){}'.format(
92 Fore.BLUE, Style.RESET_ALL))
93 continue
94
95 validn = True
96 for key in ['address', 'timezone']:
97 if key not in net[nname]:
98 print('{}Invalid network config for {}: {} is missing!{}'.format(
99 Fore.RED, nname, key, Style.RESET_ALL))
100 errors += 1
101 validn = False
102 break
103
104 if not validn:
105 continue
106
107 for n in nets:
108 if n.get('name') == nname:
109 net_obj = n
110 break
111
112 if net_obj is None:
113 nargs = {
114 'timezone': net[nname]['timezone']
115 }
116 if 'copy_from_network' in net[nname]:
117 for n in nets:
118 if n.get('name') == net[nname]['copy_from_network']:
119 nargs['copy_from_network_id'] = n.get('id')
120 break
121 net_obj = org.create_network(
122 nname, **nargs)
123
124 if net_obj is None:
125 print('{}Error creating new network {}!{}'.format(
126 Fore.RED, nname, Style.RESET_ALL))
127 errors += 1
128 continue
129
130 if 'devices' in net[nname]:
131 for dev in net[nname]['devices']:
132 serial = dev.keys()[0]
133 if 'name' not in dev[serial]:
134 print('{}Invalid device {}: name is missing!{}'.format(
135 Fore.RED, serial, Style.RESET_ALL))
136 nerrors += 1
137 continue
138
139 inv_dev = filter(
140 lambda device: device['serial'] == serial, inv)
141 if len(inv_dev) == 1:
142 dev_obj = None
143 if inv_dev[0]['networkId'] is not None and inv_dev[0]['networkId'] != net_obj.get('id'):
144 try:
145 inv_net_obj = meraki_api.Network(
146 key=config['api_key'], id=inv_dev[0]['networkId'])
147 dev_obj = meraki_api.Device(
148 key=config['api_key'], id=inv_dev[0]['serial'], net=inv_net_obj)
149
150 res = dev_obj.remove_device()
151 if not res:
152 print('{}Error removing {} from network {}!{}'.format(
153 Fore.RED, inv_dev[0]['serial'], inv_dev[0]['networkId'], Style.RESET_ALL))
154 nerrors += 1
155 continue
156 print('{}update: removed {} from network {}{}'.format(
157 Fore.YELLOW, inv_dev[0]['serial'], inv_dev[0]['networkId'], Style.RESET_ALL))
158 res = net_obj.claim_device(dev_obj)
159 if not res:
160 print('{}Error claiming {}!{}'.format(
161 Fore.RED, inv_dev[0]['serial'], Style.RESET_ALL))
162 nerrors += 1
163 continue
164 print('{}update: claimed {}{}'.format(
165 Fore.YELLOW, inv_dev[0]['serial'], Style.RESET_ALL))
166 dev_obj = res
167 except Exception as e:
168 print('{}Error updating device network membership for {}: {}{}'.format(
169 Fore.RED, inv_dev[0]['serial'], e, Style.RESET_ALL))
170 nerrors += 1
171 continue
172 elif inv_dev[0]['networkId'] is None:
173 try:
174 dev_obj = meraki_api.Device(
175 key=config['api_key'], id=inv_dev[0]['serial'], net=net_obj)
176 res = net_obj.claim_device(dev_obj)
177 if not res:
178 print('{}Error claiming device {}{}'.format(
179 Fore.RED, inv_dev[0]['serial'], Style.RESET_ALL))
180 nerrors += 1
181 continue
182 print('{}update: claimed {}{}'.format(
183 Fore.YELLOW, inv_dev[0]['serial'], Style.RESET_ALL))
184 dev_obj = res
185 except Exception as e:
186 print('{}Error claiming device {}: {}{}'.format(
187 Fore.RED, inv_dev[0]['serial'], e, Style.RESET_ALL))
188 nerrors += 1
189 continue
190
191 else:
192 dev_obj = meraki_api.Device(
193 key=config['api_key'], id=inv_dev[0]['serial'], net=net_obj)
194 print('{}ok: {} is in network{}'.format(
195 Fore.GREEN, inv_dev[0]['serial'], Style.RESET_ALL))
196
197 dev_location = net[nname]['address']
198 dev_name = dev[serial]['name']
199 if 'location' in dev[serial]:
200 dev_location += '\n' + dev[serial]['location']
201 dev_obj.update_device(
202 name=dev_name, address=dev_location, move_map_marker=True)
203 print('{}update: updated {} name and location{}'.format(
204 Fore.YELLOW, inv_dev[0]['serial'], Style.RESET_ALL))
205
206 else:
207 print('{}Error finding {} in inventory!{}'.format(
208 Fore.RED, serial, Style.RESET_ALL))
209 nerrors += 1
210
211 if 'vlans' in net[nname]:
212 # Ugh. There is no API to enable VLANs yet. So it's best to
213 # make this a manual step. We could interact over the web, but
214 # then we'd need to ask for a real user's credentials.
215 #
216 # If we copied from an existing network, then we assume that
217 # network has VLANs enabled. If not, this will fail.
218 #
219 if 'copy_from_network' not in net[nname]:
220 print('\n')
221 raw_input(
222 '!!! Enable VLANs for network "{}" manually in the dashboard (under Security appliance > Addressing & VLANs), then hit enter to proceed !!!'.format(nname))
223 print('')
224 for vlan in net[nname]['vlans']:
225 vname = vlan.keys()[0]
226 done_msg = ''
227 if int(vlan[vname]['id']) != 1:
228 vlan_obj = net_obj.create_vlan(
229 vname, vlan[vname]['id'], vlan[vname]['subnet'], vlan[vname]['appliance_ip'])
230 done_msg = '{}update: created VLAN {} (id={}, subnet={}, appliance_ip={}){}'.format(
231 Fore.YELLOW, vname, vlan[vname]['id'], vlan[vname]['subnet'], vlan[vname]['appliance_ip'], Style.RESET_ALL)
232 else:
233 vlan_obj = meraki_api.Vlan(
234 key=config['api_key'], id=1, net=net_obj)
235 done_msg = '{}ok: VLAN with ID {} exists{}'.format(
236 Fore.GREEN, vlan[vname]['id'], Style.RESET_ALL)
237 if vlan_obj is None:
238 print('{}Error creating VLAN {} (id={}, subnet={}, appliance_ip={})!{}'.format(
239 Fore.RED, vlan[vname]['id'], vlan[vname]['subnet'], vlan[vname]['appliance_ip'], Style.RESET_ALL))
240 nerrors += 1
241 continue
242 print(done_msg)
243 vargs = {}
244 for key in ['reserved_ip_ranges', 'fixed_ip_assignments', 'dns_nameservers']:
245 if key in vlan[vname]:
246 vargs[key] = vlan[vname][key]
247 res = vlan_obj.update_vlan(**vargs)
248 vargs_str = ', '.join(['{}={}'.format(k, v)
249 for k, v in vargs.iteritems()])
250 if not res:
251 print('{}Error updating VLAN {} ({})!{}'.format(
252 Fore.RED, vname, vargs_str, Style.RESET_ALL))
253 nerrors += 1
254 else:
255 print('{}update: Update VLAN {} ({}){}'.format(
256 Fore.YELLOW, vname, vargs_str, Style.RESET_ALL))
257
258 if 'ssids' in net[nname]:
259 if len(net[nname]['ssids']) > 15:
260 print('{}Only fifteen SSIDs are allowed per network!{}'.format(
261 Fore.RED, Style.RESET_ALL))
262 nerrors += 1
263 else:
264 si = 0
265 for ssid in net[nname]['ssids']:
266 sname = ssid.keys()[0]
267 ssid_obj = meraki_api.SSID(
268 key=config['api_key'], id=si, name=sname, net=net_obj)
269 sargs = {}
270 for key in ['name', 'enabled', 'auth_mode', 'encryption_mode', 'psk', 'ip_assignment_mode']:
271 if key in ssid[sname]:
272 sargs[key] = ssid[sname][key]
273 res = ssid_obj.update_ssid(**sargs)
274 sargs_str = ', '.join(
275 ['{}={}'.format(k, v) for k, v in sargs.iteritems()])
276 if not res:
277 print('{}Error updating SSID {} ({})!{}'.format(
278 Fore.RED, sname, sargs_str, Style.RESET_ALL))
279 nerrors += 1
280 else:
281 print('{}update: Update SSID {} ({}){}'.format(
282 Fore.YELLOW, sname, sargs_str, Style.RESET_ALL))
283 si += 1
284
285 if 'switches' in net[nname]:
286 for switch in net[nname]['switches']:
287 serial = switch.keys()[0]
288 dev_obj = meraki_api.Device(
289 key=config['api_key'], id=serial, net=net_obj)
290 if not dev_obj.realize():
291 print('{}Device {} is not in network {}{}'.format(
292 Fore.RED, serial, net_obj.get('name')), Style.RESET_ALL)
293 nerrors += 1
294 continue
295
296 for switchport in switch[serial]:
297 port_range = switchport.keys()[0]
298 ports = []
299 if isinstance(port_range, (int, long)):
300 port_obj = meraki_api.SwitchPort(
301 key=config['api_key'], id=port_range, dev=dev_obj)
302 ports.append(port_obj)
303 else:
304 prs = port_range.split(',')
305 for pr in prs:
306 pr = pr.strip()
307 if isinstance(pr, (int, long)):
308 port_obj = meraki_api.SwitchPort(
309 key=config['api_key'], id=pr, dev=dev_obj)
310 ports.append(pr)
311 else:
312 if '-' not in pr:
313 print('{}Port range {} is invalid.{}'.format(
314 Fore.RED, pr, Style.RESET_ALL))
315 nerrors += 1
316 continue
317
318 (start, end) = pr.split('-')
319 start = start.strip()
320 end = end.strip()
321 if not isinstance(start, (int, long)) or not isinstance(end, (int, long)):
322 print('{}Error with port range {} and {} must be integers{}'.format(
323 Fore.RED, pr, start, end, Style.RESET_ALL))
324 nerrors += 1
325 continue
326
327 if start >= end:
328 print(
329 '{}Error with port range {}; start must be less than end{}'.format(Fore.RED, pr, Style.RESET_ALL))
330 nerrors += 1
331 continue
332
333 pi = start
334 while pi <= end:
335 port_obj = meraki_api.SwitchPort(
336 key=config['api_key'], id=pi, dev=dev_obj)
337 ports.append(port_obj)
338 pi += 1
339
340 for port in ports:
341 pargs = {}
342 for key in ['name', 'tags', 'enabled', 'type', 'vlan', 'voice_vlan', 'allowed_vlans', 'poe_enabled']:
343 if key in switchport[port_range]:
344 pargs[key] = switchport[port_range][key]
345 res = port.update_switchport(**pargs)
346 pargs_str = ', '.join(
347 ['{}={}'.format(k, v) for k, v in pargs.iteritems()])
348 if not res:
349 print('{}Error updating switchport range {} ({}){}'.format(
350 Fore.RED, port_range, pargs_str, Style.RESET_ALL))
351 nerrors += 1
352 else:
353 print('{}update: Update switchport range {} ({}){}'.format(
354 Fore.YELLOW, port_range, pargs_str, Style.RESET_ALL))
355
356 if nerrors == 0:
357 print('{}ok: network {} has been setup successfully!{}\n'.format(
358 Fore.GREEN, nname, Style.RESET_ALL))
359 else:
360 print(
361 '{}Error fully configuring network {}. See the errors above for more details.{}\n'.format(Fore.RED, nname, Style.RESET_ALL))
362 errors += nerrors
363
364 if errors == 0:
365 print('{}ok: all networks have been setup successfully!{}\n'.format(
366 Fore.GREEN, Style.RESET_ALL))
367 else:
368 print('{}There were errors setting up some of the networks. See the output above for more details.{}\n'.format(
369 Fore.RED, Style.RESET_ALL))
370 sys.exit(1)
371
372
373 if __name__ == '__main__':
374 main()

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.27