Skip to content

Commit 0a3ef7a

Browse files
committed
update Meraki code to match Meraki DNE
1 parent 3da6f9a commit 0a3ef7a

27 files changed

Lines changed: 23492 additions & 0 deletions
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
"""The provided sample code in this repository will reference this file to get the
2+
information needed to connect to your lab backend. You provide this info here
3+
once and the scripts in this repository will access it as needed by the lab.
4+
Copyright (c) 2019 Cisco and/or its affiliates.
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
The above copyright notice and this permission notice shall be included in all
12+
copies or substantial portions of the Software.
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.
20+
"""
21+
22+
# Libraries
23+
from pprint import pprint
24+
import sys, os, getopt, json, time, datetime
25+
from webexteamssdk import WebexTeamsAPI
26+
import requests
27+
28+
# Get the absolute path for the directory where this file is located "here"
29+
here = os.path.abspath(os.path.dirname(__file__))
30+
31+
# Get the absolute path for the project / repository root
32+
project_root = os.path.abspath(os.path.join(here, ".."))
33+
34+
# Extend the system path to include the project root and import the env files
35+
sys.path.insert(0, project_root)
36+
import env_user # noqa
37+
38+
# WEBEX TEAMS LIBRARY
39+
teamsapi = WebexTeamsAPI(access_token=env_user.WT_ACCESS_TOKEN)
40+
41+
# MERAKI BASE URL
42+
base_url = "https://api.meraki.com/api/v0"
43+
44+
def getnetworklist():
45+
orgs = ""
46+
47+
# Get Orgs that entered Meraki API Key has access to
48+
try:
49+
# MISSION TODO
50+
orgs = requests.get(
51+
base_url + "TODO:ADD URL TO GET ORGANIZATION LIST HERE",
52+
headers={
53+
"X-Cisco-Meraki-API-Key": env_user.MERAKI_API_KEY,
54+
}
55+
)
56+
# Deserialize response text (str) to Python Dictionary object so
57+
# we can work with it
58+
orgs = json.loads(orgs.text)
59+
pprint(orgs)
60+
# END MISSION SECTION
61+
except Exception as e:
62+
pprint(e)
63+
64+
# Now get a specific network based on name added on command line
65+
networks = ""
66+
if orgs != "":
67+
for org in orgs:
68+
try:
69+
# MISSION TODO
70+
networks = requests.get(
71+
base_url + "TODO:ADD URL TO GET NETWORK LIST HERE (be sure to add organization id to the string)",
72+
headers={
73+
"X-Cisco-Meraki-API-Key": env_user.MERAKI_API_KEY,
74+
})
75+
# Deserialize response text (str) to Python Dictionary object so
76+
# we can work with it
77+
networks = json.loads(networks.text)
78+
pprint(networks)
79+
return networks
80+
# END MISSION SECTION
81+
except Exception as e:
82+
pprint(e)
83+
return ""
84+
85+
return "No Networks Found"
86+
87+
88+
def get_mx_l3_firewall_rules(network):
89+
# return the MX L3 firewall ruleset for a network
90+
try:
91+
# MISSION TODO
92+
rules = requests.get(
93+
base_url + "TODO:ADD URL TO GET L3 Firewall Rules HERE (be sure to add network id to the string)",
94+
headers={
95+
"X-Cisco-Meraki-API-Key": env_user.MERAKI_API_KEY,
96+
})
97+
pprint(network + ": " + rules.text)
98+
return rules.text
99+
# END MISSION SECTION
100+
except Exception as e:
101+
pprint("Rules lookup failed")
102+
pprint(e)
103+
return ""
104+
105+
def createbackup(networks):
106+
# create directory to place backups
107+
flag_creationfailed = True
108+
MAX_FOLDER_CREATE_TRIES = 5
109+
for i in range(0, MAX_FOLDER_CREATE_TRIES):
110+
time.sleep(2)
111+
timestamp = "{:%Y%m%d_%H%M%S}".format(datetime.datetime.now())
112+
directory = "mxfwctl_backup_" + timestamp
113+
flag_noerrors = True
114+
try:
115+
os.makedirs(directory)
116+
except Exception as e:
117+
print(e)
118+
flag_noerrors = False
119+
if flag_noerrors:
120+
flag_creationfailed = False
121+
break
122+
123+
if flag_creationfailed:
124+
pprint("Unable to create directory for backups")
125+
sys.exit(2)
126+
else:
127+
pprint('INFO: Backup directory is ' + directory)
128+
129+
# create backups - one file per network
130+
for network in networks:
131+
rules = get_mx_l3_firewall_rules(network["id"])
132+
if rules != "":
133+
filename = network["id"] + ".json"
134+
filepath = directory + "/" + filename
135+
if os.path.exists(filepath):
136+
pprint(
137+
"Cannot create backup file: name conflict " + filename
138+
)
139+
sys.exit(2)
140+
else:
141+
try:
142+
f = open(filepath, "w")
143+
except Exception as e:
144+
pprint(
145+
"Unable to open file path for writing: " + filepath
146+
)
147+
sys.exit(2)
148+
149+
150+
f.write(rules)
151+
152+
try:
153+
f.close()
154+
except Exception as e:
155+
pprintusertext(
156+
"Unable to close file path: " + filepath
157+
)
158+
sys.exit(2)
159+
160+
pprint(
161+
"INFO: Created backup for " + network["name"]
162+
)
163+
164+
teamsapi.messages.create(
165+
env_user.WT_ROOM_ID,
166+
files=[filepath],
167+
text="Network " + network["name"] + " L3 Rules Backup",
168+
)
169+
else:
170+
pprint(
171+
"WARNING: Unable to read MX ruleset for " + network["name"]
172+
)
173+
174+
return (0)
175+
176+
# Launch application
177+
if __name__ == "__main__":
178+
# Configuration parameters
179+
networks = getnetworklist()
180+
createbackup(networks)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Flask==1.1.1
2+
requests==2.22.0
3+
requests-toolbelt==0.9.1
4+
webexteamssdk==1.2
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# meraki_webhook_sample_webex_teams
2+
Sample Meraki Service That Posts to WebEx Teams
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Flask==1.1.1
2+
requests==2.22.0
3+
requests-toolbelt==0.9.1
4+
webexteamssdk==1.2
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""Set the Environment Information Needed to Access Your Lab!
2+
3+
The provided sample code in this repository will reference this file to get the
4+
information needed to connect to your lab backend. You provide this info here
5+
once and the scripts in this repository will access it as needed by the lab.
6+
7+
8+
Copyright (c) 2018 Cisco and/or its affiliates.
9+
10+
Permission is hereby granted, free of charge, to any person obtaining a copy
11+
of this software and associated documentation files (the "Software"), to deal
12+
in the Software without restriction, including without limitation the rights
13+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+
copies of the Software, and to permit persons to whom the Software is
15+
furnished to do so, subject to the following conditions:
16+
17+
The above copyright notice and this permission notice shall be included in all
18+
copies or substantial portions of the Software.
19+
20+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26+
SOFTWARE.
27+
"""
28+
29+
# Libraries
30+
from pprint import pprint
31+
from flask import Flask, json, request, render_template
32+
import sys, os, getopt, json
33+
from webexteamssdk import WebexTeamsAPI
34+
import requests
35+
36+
# Get the absolute path for the directory where this file is located "here"
37+
here = os.path.abspath(os.path.dirname(__file__))
38+
39+
# Get the absolute path for the project / repository root
40+
project_root = os.path.abspath(os.path.join(here, ".."))
41+
42+
# Extend the system path to include the project root and import the env files
43+
sys.path.insert(0, project_root)
44+
import env_user # noqa
45+
46+
# WEBEX TEAMS LIBRARY
47+
teamsapi = WebexTeamsAPI(access_token=env_user.WT_ACCESS_TOKEN)
48+
49+
# Flask App
50+
app = Flask(__name__)
51+
52+
# Webhook Receiver Code - Accepts JSON POST from Meraki and
53+
# Posts to WebEx Teams
54+
@app.route("/", methods=["POST"])
55+
def get_webhook_json():
56+
global webhook_data
57+
58+
# Webhook Receiver
59+
webhook_data = request.json
60+
pprint(webhook_data, indent=1)
61+
webhook_data = json.dumps(webhook_data)
62+
# WebEx Teams can only handle so much text so limit to 1000 chars
63+
webhook_data = webhook_data[:1000] + '...'
64+
65+
# Send Message to WebEx Teams
66+
teamsapi.messages.create(
67+
env_user.WT_ROOM_ID,
68+
text="Meraki Webhook Alert: " + webhook_data
69+
)
70+
71+
# Return success message
72+
return "WebHook POST Received"
73+
74+
# Launch application with supplied arguments
75+
def main(argv):
76+
try:
77+
opts, args = getopt.getopt(argv, "hs:", ["secret="])
78+
except getopt.GetoptError:
79+
print("webhookreceiver.py -s <secret>")
80+
sys.exit(2)
81+
for opt, arg in opts:
82+
if opt == "-h":
83+
print("webhookreceiver.py -s <secret>")
84+
sys.exit()
85+
elif opt in ("-s", "--secret"):
86+
secret = arg
87+
88+
print("secret: " + secret)
89+
90+
91+
if __name__ == "__main__":
92+
main(sys.argv[1:])
93+
app.run(host="0.0.0.0", port=5005, debug=False)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Meraki Cloud Simulator
2+
Locally run Python 3.5+ Flask based application that provides HTTP POST simulations of Location Scanning, WebHook Alerts and Splash Page (Captive Portal) Integrations.
3+
4+
To run:
5+
6+
* virtual envrionment:
7+
```
8+
python -m venv simulator
9+
source simulator/bin/activate
10+
```
11+
12+
* Install dependencies
13+
```
14+
pip install -r requirements.txt
15+
```
16+
17+
* Go!
18+
```
19+
python meraki_cloud_simulator.py
20+
```
21+
22+
Navigate to http://localhost:5001/go and select the simulator you'd like to use.
23+
24+
All of the below require a third party application to be running for these simulations to send data to. Baseline samples can be found at:
25+
26+
* Location Scanning sample receiver
27+
* Webhook Sample receiver
28+
* Captive Portal Sample
29+
30+
## Location Scanning
31+
32+
Enter number of clients and APs for the simulator provide location data, set the location of the desired map and the receiving services url and hit Launch Simulator to run. Location data for that location, AP and client set will be generated and HTTP Posted to the url provided
33+
34+
## Webhook Alerts
35+
36+
Select the alerts for this service to send and configure the HTTP listening service at the bottom and set as default receiver. Upon Save, sample alerts will be sent on a 10 second basis>
37+
38+
## Captive portal
39+
40+
Enter the URL for the captive portal to be tested and the continuation URL. Upon hitting "Simulate WiFi Connection" the service will open a new tab to the captive portal as if your local client was connecting to a Meraki SSID and serving up a captive portal
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Cisco Meraki Cloud Simulator for External Captive Portal labs."""
2+
3+
from merakicloudsimulator import merakicloudsimulator
4+
from merakicloudsimulator.meraki_settings import ORGANIZATIONS, NETWORKS
5+
from flask import render_template,jsonify,abort
6+
7+
# Module Constants & Simulated Cloud Data
8+
WEB_SERVER_HOSTNAME = "localhost"
9+
WEB_SERVER_BIND_IP = "0.0.0.0"
10+
WEB_SERVER_BIND_PORT = 5001
11+
12+
@merakicloudsimulator.route("/go", methods=["GET"])
13+
def meraki_simulator_go():
14+
return render_template("index.html")
15+
16+
# Flask micro-webservice API/URI endpoints
17+
@merakicloudsimulator.route("/organizations", methods=["GET"])
18+
def get_org_id():
19+
"""Get a list of simulated organizations."""
20+
return jsonify(ORGANIZATIONS)
21+
22+
23+
@merakicloudsimulator.route("/organizations/<organization_id>/networks", methods=["GET"])
24+
def get_networks(organization_id):
25+
"""Get the list of networks for an organization."""
26+
organization_networks = NETWORKS.get(organization_id)
27+
if organization_networks:
28+
return jsonify(organization_networks)
29+
else:
30+
abort(404)
31+
32+
if __name__ == "__main__":
33+
print(
34+
f"\n>>> "
35+
f"Open your browser and browse to "
36+
f"http://{WEB_SERVER_HOSTNAME}:{WEB_SERVER_BIND_PORT}/go "
37+
f"to configure the captive portal and simulate a client login."
38+
f" <<<\n"
39+
)
40+
41+
# Start the web server
42+
merakicloudsimulator.run(host=WEB_SERVER_BIND_IP, port=WEB_SERVER_BIND_PORT, threaded=True, debug=False)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from flask import Flask, request, render_template, redirect, jsonify, abort
2+
3+
merakicloudsimulator = Flask(__name__)
4+
5+
from merakicloudsimulator import meraki_settings
6+
from merakicloudsimulator import alert_settings
7+
from merakicloudsimulator import sample_alert_messages
8+
from merakicloudsimulator import excapsimulator
9+
from merakicloudsimulator import locationscanningsimulator
10+
from merakicloudsimulator import webhooksimulator

0 commit comments

Comments
 (0)