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

Contents of /meraki/setup_meraki_nets.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 20556 - (show annotations) (download) (as text)
Thu May 17 16:40:57 2018 UTC (22 months, 3 weeks ago) by jclarke
File MIME type: text/x-python
File size: 13058 byte(s)
Add support for copying from an existing network.

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.27