123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- #!/usr/bin/env python3
- from __future__ import print_function
- import pickle
- import os.path
- import os
- from googleapiclient.discovery import build
- from google_auth_oauthlib.flow import InstalledAppFlow
- from google.auth.transport.requests import Request
- import smtplib
- from email.message import EmailMessage
- import sys
- import re
- import subprocess
- import CLEUCreds
- from cleu.config import Config as C
- FROM = 'Joe Clarke <jclarke@cisco.com>'
- CC = 'Kris Sekula <ksekula@cisco.com>, Anthony Jesani <anjesani@cisco.com>'
- JUMP_HOSTS = [
- '10.100.252.26', '10.100.252.27', '10.100.252.28', '10.100.252.29'
- ]
- DC_MAP = {
- 'DC1': 'dc1_datastore_1',
- 'DC2': 'dc2_datastore_1',
- 'HyperFlex-DC1': 'DC1-HX-DS-01',
- 'HyperFlex-DC2': 'DC2-HX-DS-01'
- }
- DEFAULT_CLUSTER = 'FlexPod'
- HX_DCs = {
- 'HyperFlex-DC1': 1,
- 'HyperFlex-DC2': 1
- }
- IP4_SUBNET = '10.100.'
- IP6_PREFIX = '2a05:f8c0:2:'
- NETWORK_MAP = {
- 'CROSS DC VMs': {
- 'subnet': '{}252.0/24'.format(IP4_SUBNET),
- 'gw': '{}252.254'.format(IP4_SUBNET),
- 'prefix': '{}64fc::'.format(IP6_PREFIX),
- 'gw6': '{}64fc::fe'.format(IP6_PREFIX)
- },
- 'DC1 ONLY VMs': {
- 'subnet': '{}253.0/24'.format(IP4_SUBNET),
- 'gw': '{}253.254'.format(IP4_SUBNET),
- 'prefix': '{}64fd::'.format(IP6_PREFIX),
- 'gw6': '{}64fd::fe'.format(IP6_PREFIX)
- },
- 'DC2 ONLY VMs': {
- 'subnet': '{}254.0/24'.format(IP4_SUBNET),
- 'gw': '{}254.254'.format(IP4_SUBNET),
- 'prefix': '{}64fe::'.format(IP6_PREFIX),
- 'gw6': '{}64fe::fe'.format(IP6_PREFIX)
- }
- }
- OSTYPE_LIST = [
- (r'(?i)ubuntu', 'ubuntu64Guest'),
- (r'(?i)windows 10', 'windows9_64Guest'),
- (r'(?i)windows 2012', 'windows8Server64Guest'),
- (r'(?i)windows 201(6|9)', 'windows9Server64Guest'),
- (r'(?i)debian 8', 'debian8_64Guest'),
- (r'(?i)debian', 'debian9_64Guest'),
- (r'(?i)centos 7', 'centos7_64Guest'),
- (r'(?i)centos', 'centos8_64Guest'),
- (r'(?i)red hat', 'rhel7_64Guest'),
- (r'(?i)linux', 'other3xLinux64Guest')
- ]
- DNS1 = '10.100.253.6'
- DNS2 = '10.100.254.6'
- NTP1 = '10.128.0.1'
- NTP2 = '10.128.0.2'
- VCENTER = 'https://' + C.VCENTER
- DOMAIN = C.DNS_DOMAIN
- AD_DOMAIN = C.AD_DOMAIN
- SMTP_SERVER = C.SMTP_SERVER
- SYSLOG = SMTP_SERVER
- ISO_DS = 'dc1_datastore_1'
- ISO_DS_HX1 = 'DC1-HX-DS-01'
- ISO_DS_HX2 = 'DC2-HX-DS-01'
- VPN_SERVER = C.VPN_SERVER
- VPN_SERVER_IP = C.VPN_SERVER_IP
- ANSIBLE_PATH = '/home/jclarke/src/git/ciscolive/automation/cleu-ansible-n9k'
- UPDATE_DNS_PATH = '/home/jclarke'
- DATACENTER = 'CiscoLive'
- CISCOLIVE_YEAR = C.CISCOLIVE_YEAR
- PW_RESET_URL = C.PW_RESET_URL
- SPREADSHEET_ID = '1ExTNQJ7SArHSJKfPOj_x1O2aTj76dHjlG8kCDHW39hw'
- SHEET_HOSTNAME = 0
- SHEET_OS = 1
- SHEET_OVA = 2
- SHEET_CONTACT = 4
- SHEET_CPU = 5
- SHEET_RAM = 6
- SHEET_DISK = 7
- SHEET_NICS = 8
- SHEET_DC = 11
- SHEET_IP = 12
- SHEET_VLAN = 13
- def main():
- if len(sys.argv) != 2:
- print('usage: {} ROW_RANGE'.format(sys.argv[0]))
- sys.exit(1)
- if not os.path.exists('gs_token.pickle'):
- print('ERROR: Google Sheets token does not exist! Please re-auth the app first.')
- sys.exit(1)
- creds = None
- with open('gs_token.pickle', 'rb') as token:
- creds = pickle.load(token)
- if 'VMWARE_USER' not in os.environ or 'VMWARE_PASSWORD' not in os.environ:
- print('ERROR: VMWARE_USER and VMWARE_PASSWORD environment variables must be set prior to running!')
- sys.exit(1)
- gs_service = build('sheets', 'v4', credentials=creds)
- vm_sheet = gs_service.spreadsheets()
- vm_result = vm_sheet.values().get(spreadsheetId=SPREADSHEET_ID,
- range=sys.argv[1]).execute()
- vm_values = vm_result.get('values', [])
- if not vm_values:
- print('ERROR: Did not read anything from Google Sheets!')
- sys.exit(1)
- (rstart, rend) = sys.argv[1].split(':')
- i = int(rstart) - 1
- users = {}
- for row in vm_values:
- i += 1
- try:
- owners = row[SHEET_CONTACT].strip().split(',')
- name = row[SHEET_HOSTNAME].strip()
- opsys = row[SHEET_OS].strip()
- is_ova = row[SHEET_OVA].strip()
- cpu = int(row[SHEET_CPU].strip())
- mem = int(row[SHEET_RAM].strip())
- disk = int(row[SHEET_DISK].strip())
- dc = row[SHEET_DC].strip()
- vlan = row[SHEET_VLAN].strip()
- ip = row[SHEET_IP].strip()
- except Exception as e:
- print('WARNING: Failed to process malformed row {}: {}'.format(i, e))
- continue
- if name == '' or ip == '' or dc == '':
- print('WARNING: Ignorning malformed row {}'.format(i))
- continue
- for owner in owners:
- owner = owner.strip()
- if owner not in users:
- users[owner] = []
- vm = {
- 'name': name.upper(),
- 'os': opsys,
- 'mem': mem,
- 'is_ova': is_ova,
- 'cpu': cpu,
- 'disk': disk,
- 'vlan': vlan,
- 'ip': ip,
- 'dc': dc
- }
- users[owner].append(vm)
- for user, vms in users.items():
- m = re.search(r'<?(\S+)@', user)
- username = m.group(1)
- body = 'Please find the CLEU Data Centre Access details below\r\n\r\n'
- body += 'Before you can access the Data Centre from remote, AnyConnect to {} and login with {} / {}\r\n'.format(
- VPN_SERVER, CLEUCreds.VPN_USER, CLEUCreds.VPN_PASS)
- body += 'Once connected, your browser should redirect you to the password change tool. If not go to {} and login with {} and password {}\r\n'.format(
- PW_RESET_URL, username, CLEUCreds.DEFAULT_USER_PASSWORD)
- body += 'Reset your password. You must use a complex password that contains lower and uppercase letters, numbers, or a special character.\r\n'
- body += 'After resetting your password, drop the VPN and reconnect to {} with {} and the new password you just set.\r\n\r\n'.format(
- VPN_SERVER, username)
- body += 'You can use any of the following Windows Jump Hosts to access the data centre using RDP:\r\n\r\n'
- for js in JUMP_HOSTS:
- body += '{}\r\n'.format(js)
- body += '\r\nIf a Jump Host is full, try the next one.\r\n\r\n'
- body += 'Your login is {} (or {}@{} on Windows). Your password is the same you used for the VPN.\r\n\r\n'.format(
- username, username, AD_DOMAIN)
- body += 'The network details for your VM(s) are:\r\n\r\n'
- body += 'DNS1 : {}\r\n'.format(DNS1)
- body += 'DNS2 : {}\r\n'.format(DNS2)
- body += 'NTP1 : {}\r\n'.format(NTP1)
- body += 'NTP2 : {}\r\n'.format(NTP2)
- body += 'DNS DOMAIN : {}\r\n'.format(DOMAIN)
- body += 'SMTP : {}\r\n'.format(SMTP_SERVER)
- body += 'AD DOMAIN : {}\r\n'.format(AD_DOMAIN)
- body += 'Syslog/NetFlow: {}\r\n\r\n'.format(SYSLOG)
- body += 'vCenter is {}. You MUST use the web client. Your AD credentials above will work there. VMs that don\'t require an OVA have been pre-created, but require installation and configuration. If you use an OVA, you will need to deploy it yourself.\r\n\r\n'.format(
- VCENTER)
- body += 'Your VM details are as follows. DNS records have been pre-created for the VM name (i.e., hostname) below:\r\n\r\n'
- created = {}
- for vm in vms:
- iso_ds = ISO_DS
- cluster = DEFAULT_CLUSTER
- if vm['dc'] in HX_DCs:
- if vm['dc'].endswith('2'):
- iso_ds = ISO_DS_HX2
- else:
- iso_ds = ISO_DS_HX1
- cluster = vm['dc']
- is_ova = False
- if vm['is_ova'].lower() == 'true' or vm['is_ova'].lower() == 'yes':
- is_ova = True
- ostype = None
- for ostypes in OSTYPE_LIST:
- if re.search(ostypes[0], vm['os']):
- ostype = ostypes[1]
- break
- if not is_ova and ostype is None:
- print('WARNING: Did not find OS type for {}'.format(vm['os']))
- continue
- if not is_ova and vm['vlan'] != '' and vm['name'] not in created:
- print('===Adding VM for {}==='.format(vm['name']))
- mem = vm['mem'] * 1024
- scsi = 'lsilogic'
- if re.search(r'^win', ostype):
- scsi = 'lsilogicsas'
- os.chdir(ANSIBLE_PATH)
- command = ['ansible-playbook', '-i', 'inventory/hosts', '-e', 'vmware_cluster=\'{}\''.format(cluster), '-e', 'vmware_datacenter=\'{}\''.format(DATACENTER), '-e', 'guest_id={}'.format(ostype), '-e', 'guest_name={}'.format(vm['name']), '-e', 'guest_size={}'.format(
- vm['disk']), '-e', 'guest_mem={}'.format(mem), '-e', 'guest_cpu={}'.format(vm['cpu']), '-e', 'guest_datastore={}'.format(DC_MAP[vm['dc']]), '-e', 'guest_network=\'{}\''.format(vm['vlan']), '-e', 'guest_scsi={}'.format(scsi), '-e', 'ansible_python_interpreter={}'.format(sys.executable), 'add-vm-playbook.yml']
- p = subprocess.Popen(
- command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- output = ''
- for c in iter(lambda: p.stdout.read(1), b''):
- output += c.decode('utf-8')
- p.wait()
- rc = p.returncode
- if rc != 0:
- print(
- '\n\n***ERROR: Failed to add VM {}\n{}!'.format(vm['name'], output))
- continue
- print('===DONE===')
- if vm['name'] not in created:
- print('===Adding DNS record for {} ==> {}==='.format(
- vm['name'], vm['ip']))
- os.chdir(UPDATE_DNS_PATH)
- command = ['{}/update_dns.py'.format(UPDATE_DNS_PATH), '--ip',
- vm['ip'], '--host', vm['name']]
- p = subprocess.Popen(
- command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- output = ''
- for c in iter(lambda: p.stdout.read(1), b''):
- output += c.decode('utf-8')
- p.wait()
- rc = p.returncode
- if rc != 0:
- print('\n\n***ERROR: Failed to add DNS record!\n{}'.format(output))
- continue
- print('===DONE===')
- octets = vm['ip'].split('.')
- body += '{} : {} (v6: {}{}) (Network: {}, Subnet: {}, GW: {}, v6 Prefix: {}/64, v6 GW: {}) : Deploy to the {} datastore in the "{}" cluster.\r\n\r\nFor this VM upload ISOs to the {} datastore. There is an "ISOs" folder there already.\r\n\r\n'.format(
- vm['name'], vm['ip'], NETWORK_MAP[vm['vlan']]['prefix'], format(int(octets[3]), 'x'), vm['vlan'], NETWORK_MAP[vm['vlan']]['subnet'], NETWORK_MAP[vm['vlan']]['gw'], NETWORK_MAP[vm['vlan']]['prefix'], NETWORK_MAP[vm['vlan']]['gw6'], DC_MAP[vm['dc']], cluster, iso_ds)
- created[vm['name']] = True
- body += 'Let us know via Webex Teams if you need any other details.\r\n\r\n'
- body += 'Joe, Kris and Anthony\r\n\r\n'
- subject = 'Cisco Live Europe {} Data Centre Access Info'.format(
- CISCOLIVE_YEAR)
- smtp = smtplib.SMTP(SMTP_SERVER)
- msg = EmailMessage()
- msg.set_content(body)
- msg['Subject'] = subject
- msg['From'] = FROM
- msg['To'] = user
- msg['Cc'] = CC + ',' + FROM
- smtp.send_message(msg)
- smtp.quit()
- if __name__ == '__main__':
- main()
|