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

Annotation of /meraki/setup_meraki_nets.py

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.27