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

Contents of /meraki/setup_meraki_nets.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 20569 - (show annotations) (download) (as text)
Tue Oct 30 08:24:17 2018 UTC (17 months, 1 week ago) by jclarke
File MIME type: text/x-python
File size: 17688 byte(s)
Add more output.

Color-code output to show more progress as the script runs.

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.27