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

Annotation of /meraki/setup_meraki_nets.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 20569 - (hide 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 jclarke 20550 #!/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 jclarke 20569 import colorama import Fore
33     import Back
34     import Style
35 jclarke 20550
36 jclarke 20569 BANNER = '[{}] **********************************************************'
37 jclarke 20550
38 jclarke 20569
39 jclarke 20550 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 jclarke 20569 colorama.init()
49    
50 jclarke 20550 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 jclarke 20569 print(BANNER.format('Loading config file'))
55 jclarke 20550 with open(args.config, 'r') as c:
56     config = yaml.load(c)
57 jclarke 20569 print('{}ok{}\n'.format(Fore.GREEN, Style.RESET_ALL))
58 jclarke 20550
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 jclarke 20569 print(BANNER.format('Configuring network {}'.format(nname)))
91 jclarke 20550 if configure_nets is not None and nname not in configure_nets:
92 jclarke 20569 print('{}skipping (not in specified network list){}'.format(
93     Fore.BLUE, Style.RESET_ALL))
94 jclarke 20550 continue
95    
96     validn = True
97     for key in ['address', 'timezone']:
98     if key not in net[nname]:
99 jclarke 20569 print('{}Invalid network config for {}: {} is missing!{}'.format(
100     Fore.RED, nname, key, Style.RESET_ALL))
101 jclarke 20550 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 jclarke 20556 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 jclarke 20550 net_obj = org.create_network(
123 jclarke 20556 nname, **nargs)
124 jclarke 20550
125     if net_obj is None:
126 jclarke 20569 print('{}Error creating new network {}!{}'.format(
127     Fore.RED, nname, Style.RESET_ALL))
128 jclarke 20550 errors += 1
129     continue
130    
131 jclarke 20552 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 jclarke 20569 print('{}Invalid device {}: name is missing!{}'.format(
136     Fore.RED, serial, Style.RESET_ALL))
137 jclarke 20552 nerrors += 1
138     continue
139 jclarke 20550
140 jclarke 20553 inv_dev = filter(
141     lambda device: device['serial'] == serial, inv)
142 jclarke 20552 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 jclarke 20550
151 jclarke 20552 res = dev_obj.remove_device()
152     if not res:
153 jclarke 20569 print('{}Error removing {} from network {}!{}'.format(
154     Fore.RED, inv_dev[0]['serial'], inv_dev[0]['networkId'], Style.RESET_ALL))
155 jclarke 20552 nerrors += 1
156     continue
157 jclarke 20569 print('{}update: removed {} from network {}{}'.format(
158     Fore.YELLOW, inv_dev[0]['serial'], inv_dev[0]['networkId'], Style.RESET_ALL))
159 jclarke 20552 res = net_obj.claim_device(dev_obj)
160     if not res:
161 jclarke 20569 print('{}Error claiming {}!{}'.format(
162     Fore.RED, inv_dev[0]['serial'], Style.RESET_ALL))
163 jclarke 20552 nerrors += 1
164     continue
165 jclarke 20569 print('{}update: claimed {}{}'.format(
166     Fore.YELLOW, inv_dev[0]['serial'], Style.RESET_ALL))
167 jclarke 20565 dev_obj = res
168 jclarke 20552 except Exception as e:
169 jclarke 20569 print('{}Error updating device network membership for {}: {}{}'.format(
170     Fore.RED, inv_dev[0]['serial'], e, Style.RESET_ALL))
171 jclarke 20550 nerrors += 1
172     continue
173 jclarke 20552 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 jclarke 20569 print('{}Error claiming device {}{}'.format(
180     Fore.RED, inv_dev[0]['serial'], Style.RESET_ALL))
181 jclarke 20552 nerrors += 1
182     continue
183 jclarke 20569 print('{}update: claimed {}{}'.format(
184     Fore.YELLOW, inv_dev[0]['serial'], Style.RESET_ALL))
185 jclarke 20565 dev_obj = res
186 jclarke 20552 except Exception as e:
187 jclarke 20569 print('{}Error claiming device {}: {}{}'.format(
188     Fore.RED, inv_dev[0]['serial'], e, Style.RESET_ALL))
189 jclarke 20550 nerrors += 1
190     continue
191 jclarke 20552
192     else:
193 jclarke 20550 dev_obj = meraki_api.Device(
194     key=config['api_key'], id=inv_dev[0]['serial'], net=net_obj)
195 jclarke 20569 print('{}ok: {} is in network{}'.format(
196     Fore.GREEN, inv_dev[0]['serial'], Style.RESET_ALL))
197 jclarke 20550
198 jclarke 20552 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 jclarke 20557 name=dev_name, address=dev_location, move_map_marker=True)
204 jclarke 20569 print('{}update: updated {} name and location{}'.format(
205     Style.YELLOW, inv_dev[0]['serial'], Style.RESET_ALL))
206 jclarke 20552
207 jclarke 20550 else:
208 jclarke 20569 print('{}Error finding {} in inventory!{}'.format(
209     Fore.RED, serial, Style.RESET_ALL))
210 jclarke 20552 nerrors += 1
211 jclarke 20550
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 jclarke 20554 #
217 jclarke 20556 # 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 jclarke 20550 for vlan in net[nname]['vlans']:
226     vname = vlan.keys()[0]
227 jclarke 20569 done_msg = ''
228 jclarke 20550 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 jclarke 20569 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 jclarke 20550 else:
234     vlan_obj = meraki_api.Vlan(
235     key=config['api_key'], id=1, net=net_obj)
236 jclarke 20569 done_msg = '{}ok: VLAN with ID {} exists{}'.format(
237     Fore.GREEN, vlan[vname]['id'], Style.RESET_ALL)
238 jclarke 20550 if vlan_obj is None:
239 jclarke 20569 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 jclarke 20550 nerrors += 1
242     continue
243 jclarke 20569 print(done_msg)
244 jclarke 20550 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 jclarke 20569 vargs_str = ', '.join(['{}={}'.format(k, v)
250     for k, v in vargs.iteritems()])
251 jclarke 20550 if not res:
252 jclarke 20569 print('{}Error updating VLAN {} ({})!{}'.format(
253     Fore.RED, vname, vargs_str, Style.RESET_ALL))
254 jclarke 20550 nerrors += 1
255 jclarke 20569 else:
256     print('{}update: Update VLAN {} ({}){}'.format(
257     Fore.YELLOW, vname, vargs_str, Style.RESET_ALL))
258 jclarke 20550
259     if 'ssids' in net[nname]:
260 jclarke 20554 if len(net[nname]['ssids']) > 15:
261 jclarke 20569 print('{}Only fifteen SSIDs are allowed per network!{}'.format(
262     Fore.RED, Style.RESET_ALL))
263 jclarke 20550 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 jclarke 20569 sargs_str = ', '.join(['{}={}'.format(format(k, v)
276     for k, v in sargs.iteritems()]))
277 jclarke 20550 if not res:
278 jclarke 20569 print('{}Error updating SSID {} ({})!{}'.format(
279     Fore.RED, sname, sargs_str, Style.RESET_ALL))
280 jclarke 20550 nerrors += 1
281 jclarke 20569 else:
282     print('{}update: Update SSID {} ({}){}'.format(
283     Fore.YELLOW, sname, sargs_str, Style.RESET_ALL))
284 jclarke 20550 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 jclarke 20569 print('{}Device {} is not in network {}{}'.format(
293     Fore.RED, serial, net_obj.get('name')), Style.RESET_ALL)
294 jclarke 20550 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 jclarke 20569 print('{}Port range {} is invalid.{}'.format(
315     Fore.RED, pr, Style.RESET_ALL))
316 jclarke 20550 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 jclarke 20569 print('{}Error with port range {} and {} must be integers{}'.format(
324     Fore.RED, pr, start, end, Style.RESET_ALL))
325 jclarke 20550 nerrors += 1
326     continue
327    
328     if start >= end:
329     print(
330 jclarke 20569 '{}Error with port range {}; start must be less than end{}'.format(Fore.RED, pr, Style.RESET_ALL))
331 jclarke 20550 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 jclarke 20569 pargs_str = ', '.join(
348     ['{}={}'.format(k, v) for k, v in pargs.iteritems()])
349 jclarke 20550 if not res:
350 jclarke 20569 print('{}Error updating switchport range {} ({}){}'.format(
351     Fore.RED, port_range, pargs_str, Style.RESET_ALL))
352 jclarke 20550 nerrors += 1
353 jclarke 20569 else:
354     print('{}update: Update switchport range {} ({}){}'.format(
355     Fore.YELLOW, port_range, pargs_str, Style.RESET_ALL))
356 jclarke 20550
357     if nerrors == 0:
358 jclarke 20569 print('{}ok: network {} has been setup successfully!{}\n'.format(
359     Fore.GREEN, nname, Style.RESET_ALL))
360 jclarke 20550 else:
361     print(
362 jclarke 20569 '{}Error fully configuring network {}. See the errors above for more details.{}\n'.format(Fore.RED, nname, Style.RESET_ALL))
363 jclarke 20550 errors += nerrors
364    
365     if errors == 0:
366 jclarke 20569 print('{}ok: all networks have been setup successfully!{}\n'.format(
367     Fore.GREEN, Style.RESET_ALL))
368 jclarke 20550 else:
369 jclarke 20569 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 jclarke 20550 sys.exit(1)
372    
373    
374     if __name__ == '__main__':
375     main()

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.27