diff --git a/.gitignore b/.gitignore index aa69ef3..3aa3450 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ env/ *.code-workspace *.sha256 terraform.tfstate + +test_data/**.csv +./idea/* +**/__pycache__/ diff --git a/src/geocode.py b/src/geocode.py new file mode 100644 index 0000000..4385f68 --- /dev/null +++ b/src/geocode.py @@ -0,0 +1,33 @@ +from geopy import Nominatim +from timezonefinder import TimezoneFinder + +def geocode(address) -> str: + geolocator = Nominatim(user_agent="geocode") + location = geolocator.geocode(address) + try: + latitude, longitude = location.latitude, location.longitude + except ValueError: + raise ValueError('geocode unable to find latitude & longitude for {address}'.format(address=address)) + return latitude, longitude + +def find_country_code(gps) -> tuple: + latitude = gps[0] + longitude = gps[1] + geolocator = Nominatim(user_agent="geocode") + location = geolocator.reverse([latitude, longitude]) + country_code=location.raw['address']['country_code'] + + return country_code.upper() + +def find_timezone(gps) -> tuple: + tf_init = TimezoneFinder() + latitude = gps[0] + longitude = gps[1] + try: + timezone_name = tf_init.timezone_at(lat=latitude, lng=longitude) + except ValueError: + raise ValueError('The coordinates were out of bounds {latitude}:{longitude}'.format(lat=latitude,lng=longitude)) + if timezone_name is None: + raise ValueError('GPS coordinates did not match a time_zone') + + return timezone_name \ No newline at end of file diff --git a/src/juniper.py b/src/juniper.py new file mode 100644 index 0000000..80f31ab --- /dev/null +++ b/src/juniper.py @@ -0,0 +1,127 @@ +import sys, requests, json + +# Mist CRUD operations +class Admin(object): + def __init__(self, token=''): + self.session = requests.Session() + self.headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Token ' + token + } + + def post(self, url, payload, timeout=60): + url = 'https://api.eu.mist.com{}'.format(url) + session = self.session + headers = self.headers + + print('POST {}'.format(url)) + response = session.post(url, headers=headers, json=payload) + + if response.status_code != 200: + print('Failed to POST') + print('\tURL: {}'.format(url)) + print('\tPayload: {}'.format(payload)) + print('\tResponse: {} ({})'.format(response.text, response.status_code)) + + return False + + return json.loads(response.text) + + def put(self, url, payload): + url = 'https://api.eu.mist.com{}'.format(url) + session = self.session + headers = self.headers + + print('PUT {}'.format(url)) + response = session.put(url, headers=headers, json=payload) + + if response.status_code != 200: + print('Failed to PUT') + print('\tURL: {}'.format(url)) + print('\tPayload: {}'.format(payload)) + print('\tResponse: {} ({})'.format(response.text, response.status_code)) + + return False + + return json.loads(response.text) + + +# Main function +def juniper_script( + data, + mist_api_token='', + org_id=''): + + show_more_details = True # Configure True/False to enable/disable additional logging of the API response objects + + + # Check for required variables + if mist_api_token == '': + print('Please provide your Mist API token as mist_api_token') + sys.exit(1) + elif org_id == '': + print('Please provide your Mist Organization UUID as org_id') + sys.exit(1) + + # Establish Mist session + admin = Admin(mist_api_token) + + # Create each site from the CSV file + for d in data: + # Variables + site_id = None + site = {'name': d.get('Site Name', ''), + 'address': d.get('Site Address', ''), + "latlng": {"lat": d.get('gps', '')[0], "lng": d.get('gps', '')[1]}, + "country_code": d.get('country_code', ''), + "timezone": d.get('time_zone', ''), + } + + # MOJ specific attributes + site_setting = { + + "vars": { + "Enable GovWifi": d.get('Enable GovWifi', ''), + "Enable MoJWifi": d.get('Enable MoJWifi', ''), + "Weird NACS Radius Key": d.get('Weird NACS Radius Key', ''), + "GovWifi Radius Key": d.get('GovWifi Radius Key', '') + + } + + } + + + + print('Calling the Mist Create Site API...') + result = admin.post('/api/v1/orgs/' + org_id + '/sites', site) + if result == False: + print('Failed to create site {}'.format(site['name'])) + print('Skipping remaining operations for this site...') + print('\n\n==========\n\n') + + continue + else: + site_id = result['id'] + print('Created site {} ({})'.format(site['name'], site_id)) + + if show_more_details: + print('\nRetrieving the JSON response object...') + print(json.dumps(result, sort_keys=True, indent=4)) + print('\nUsing id in the Mist Update Setting API request') + + print() + + # Update Site Setting + print('Calling the Mist Update Setting API...') + result = admin.put('/api/v1/sites/' + site_id + '/setting', + site_setting) + if result == False: + print('Failed to update site setting {} ({})'.format(site['name'], site_id)) + else: + print('Updated site setting {} ({})'.format(site['name'], site_id)) + + if show_more_details: + print('\nRetrieving the JSON response object...') + print(json.dumps(result, sort_keys=True, indent=4)) + + print('\n\n==========\n\n') diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..61c9ccd --- /dev/null +++ b/src/main.py @@ -0,0 +1,49 @@ +from juniper import juniper_script +import os, csv +from geocode import geocode, find_timezone, find_country_code + + +# Convert CSV file to JSON object. +def csv_to_json(file_path): + csv_rows = [] + with open(file_path) as csvfile: + reader = csv.DictReader(csvfile, skipinitialspace=True, quotechar='"') + title = reader.fieldnames + + for row in reader: + csv_rows.extend([ {title[i]: row[title[i]] for i in range(len(title))} ]) + + return csv_rows + +if __name__ == '__main__': + + csv_file_path=os.getcwd() + '/../test_data/sites_with_clients.csv' + + # Convert CSV to valid JSON + data = csv_to_json(csv_file_path) + if data == None or data == []: + raise ValueError('Failed to convert CSV file to JSON. Exiting script.') + + + # Create each site from the CSV file + for d in data: + # Variables + site_id = None + site = {'name': d.get('Site Name', ''), + 'address': d.get('Site Name', '') + } + + gps = geocode(d.get('Site Address', '')) + country_code = find_country_code(gps) + time_zone = find_timezone(gps) + + # Adding new key-value pairs to the dictionary + d['gps'] = gps + d['country_code'] = country_code + d['time_zone'] = time_zone + + juniper_script( + mist_api_token=os.environ['MIST_API_TOKEN'], + org_id=os.environ['ORG_ID'], + data=data + ) diff --git a/src/requirements.txt b/src/requirements.txt new file mode 100644 index 0000000..cf38b7e --- /dev/null +++ b/src/requirements.txt @@ -0,0 +1,3 @@ +requests==2.21.0 +geopy==2.4.1 +timezonefinder==6.2.0 \ No newline at end of file diff --git a/test_data/.gitkeep b/test_data/.gitkeep new file mode 100644 index 0000000..e69de29