3 # Copyright (c) 2019 AT&T Intellectual Property. All Rights Reserved.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 # License for the specific language governing permissions and limitations
19 REC_create.py - This workflow is used to create a REC POD by way of the remote-installer
20 container. The remote-installer is started if it is not running. Parameters passed to
21 this script (via the INPUT.yaml file) are:
22 iso_primary - the main installer.iso file to use
23 iso_secondary - the secondary bootcd.iso file
24 input_yaml - the YAML file passed to remote_installer
25 rc_host - the IP address or DNS name of the RC
30 import requests, urllib3
31 import os, sys, time, yaml
34 WORKDIR = os.path.abspath(os.path.dirname(__file__))
35 RI_NAME = 'remote-installer'
36 RI_IMAGE = 'nexus3.akraino.org:10003/akraino/remote-installer:latest'
37 RI_DIR = '/workflow/remote-installer'
38 CERT_DIR = RI_DIR + '/certificates'
39 EXTERNALROOT = '/data'
44 ADMIN_PASSWD = 'recAdm1n'
48 def start(ds, **kwargs):
49 # Read the user input from the POST
51 urllib3.disable_warnings()
52 yaml = read_yaml(WORKDIR + '/INPUT.yaml')
53 REC_ISO_IMAGE_NAME = yaml['iso_primary']
54 REC_PROVISIONING_ISO_NAME = yaml['iso_secondary']
55 INPUT_YAML_URL = yaml['input_yaml']
56 HOST_IP = yaml['rc_host']
57 CLOUDNAME = 'CL-'+POD.POD
58 ISO = '%s/images/install-%s.iso' % (RI_DIR, POD.POD)
59 BOOTISO = '%s/images/bootcd-%s.iso' % (RI_DIR, POD.POD)
60 USERCONF = '%s/user-configs/%s/user_config.yaml' % (RI_DIR, CLOUDNAME)
62 print('-----------------------------------------------------------------------------------------------')
63 print(' POD is '+POD.POD)
64 print(' CLOUDNAME is '+CLOUDNAME)
65 print(' WORKDIR is '+WORKDIR)
66 print(' HOST_IP is '+HOST_IP)
67 print(' EXTERNALROOT is '+EXTERNALROOT)
68 print(' REC_ISO_IMAGE_NAME is '+REC_ISO_IMAGE_NAME)
69 print('REC_PROVISIONING_ISO_NAME is '+REC_PROVISIONING_ISO_NAME)
70 print(' INPUT_YAML_URL is '+INPUT_YAML_URL)
72 print(' BOOTISO is '+BOOTISO)
73 print(' USERCONF is '+USERCONF)
74 print('-----------------------------------------------------------------------------------------------')
77 initialize_RI(CLOUDNAME)
79 # Fetch the three files into WORKDIR
80 fetchURL(REC_ISO_IMAGE_NAME, WORKDIR + '/install.iso');
81 fetchURL(REC_PROVISIONING_ISO_NAME, WORKDIR + '/bootcd.iso');
82 fetchURL(INPUT_YAML_URL, WORKDIR + '/user_config.yaml');
84 # Link files to RI_DIR with unique names
85 os.link(WORKDIR + '/install.iso', ISO)
86 os.link(WORKDIR + '/bootcd.iso', BOOTISO)
87 os.link(WORKDIR + '/user_config.yaml', USERCONF)
88 PWFILE = '%s/user-configs/%s/admin_passwd' % (RI_DIR, CLOUDNAME)
89 with open(PWFILE, "w") as f:
90 f.write(ADMIN_PASSWD + '\n')
92 # Start the remote_installer
93 client = docker.from_env()
94 namefilt = { 'name': RI_NAME }
95 ri = client.containers.list(filters=namefilt)
97 print(RI_NAME + ' is not running.')
101 print(RI_NAME + ' is running.')
104 # Send request to remote_installer
105 id = send_request(HOST_IP, CLOUDNAME, ISO, BOOTISO)
107 # Wait up to WAIT_TIME minutes for completion
108 if wait_for_completion(HOST_IP, id, WAIT_TIME):
109 print('Installation failed after %d minutes.' % (WAIT_TIME))
114 for iso in (WORKDIR + '/install.iso', ISO, WORKDIR + '/bootcd.iso', BOOTISO):
118 print('Installation complete!')
119 # sys.exit(0) Don't exit as this will cause the task to fail!
122 def read_yaml(input_file):
123 print('Reading '+input_file+' ...')
124 with open(input_file, 'r') as stream:
126 return yaml.safe_load(stream)
127 except yaml.YAMLError as exc:
131 def send_request(ri_ip, CLOUDNAME, ISO, BOOTISO):
132 URL = 'https://%s:%d/v1/installations' % (ri_ip, API_PORT)
133 print('Sending request to '+URL+' ...')
134 headers = {'Content-type': 'application/json'}
136 'cloud-name': CLOUDNAME,
137 'iso': os.path.basename(ISO),
138 'provisioning-iso': os.path.basename(BOOTISO)
140 certs = (CERT_DIR+'/clientcert.pem', CERT_DIR+'/clientkey.pem')
141 response = requests.post(URL, json=content, headers=headers, cert=certs, verify=False)
143 return response.json().get('uuid')
145 def create_podevent(msg='Default msg', level='INFO'):
146 API_HOST = 'http://arc-api:8080'
147 if os.environ.get('LOGGING_USER') and os.environ.get('LOGGING_PASSWORD'):
148 payload = {'name': os.environ['LOGGING_USER'], 'password': os.environ['LOGGING_PASSWORD']}
149 response = requests.post(API_HOST+'/api/v1/login', json=payload)
150 token = response.headers['X-ARC-Token']
151 headers = {'X-ARC-Token': token}
152 payload = {'uuid': POD.POD, 'level': level, 'message': msg}
153 response = requests.post(API_HOST+'/api/v1/podevent', headers=headers, json=payload)
155 def wait_for_completion(ri_ip, id, ntimes):
157 Wait (up to ntimes minutes) for the remote_installer to finish.
158 Any status other than 'completed' is considered a failure.
161 URL = 'https://%s:%d/v1/installations/%s/state' % (ri_ip, API_PORT, id)
162 certs = (CERT_DIR+'/clientcert.pem', CERT_DIR+'/clientkey.pem')
164 while status == 'ongoing' and ntimes > 0:
166 response = requests.get(URL, cert=certs, verify=False)
169 str(j.get('status')),
170 str(j.get('percentage')),
171 str(j.get('description'))
173 event = 'Status is %s (%s) %s' % t
174 print('%s: %s' % (datetime.datetime.now().strftime('%x %X'), event))
175 if event != lastevent:
176 create_podevent(event)
178 status = j.get('status')
180 return status != 'completed'
182 def fetchURL(url, dest):
183 print('Fetching '+url+' ...')
184 r = requests.get(url)
185 with open(dest, 'wb') as f1:
188 def initialize_RI(CLOUDNAME):
189 """ Create the directory structure needed by the remote-installer """
192 RI_DIR+'/certificates',
194 RI_DIR+'/installations',
195 RI_DIR+'/user-configs',
196 RI_DIR+'/user-configs/'+CLOUDNAME
199 if not os.path.isdir(dir):
203 def start_RI(client):
205 Start the remote-installer container (assumed to already be built somewhere).
206 Before starting, make sure the certificates directory is populated. If not,
207 generate some self-signed certificates.
209 # If needed, create certificates (11 files) in RI_DIR/certificates
210 if not os.path.exists(CERT_DIR+'/clientcert.pem') or not os.path.exists(CERT_DIR+'/clientkey.pem'):
211 print('Generating some self-signed certificates.')
212 script = WORKDIR + '/gencerts.sh'
213 cmd = 'bash %s %s' % (script, RI_DIR+'/certificates')
214 print('os.system('+cmd+')')
217 print('Starting %s.' % RI_NAME)
219 'API_PORT': API_PORT, 'HOST_ADDR': HOST_IP, 'HTTPS_PORT': HTTPS_PORT,
220 'PW': ADMIN_PASSWD, 'SSH_PORT': 22222
223 EXTERNALROOT+RI_DIR: {'bind': '/opt/remoteinstaller', 'mode': 'rw'}
226 c = client.containers.run(
229 network_mode=NETWORK,
237 # Wait 5 minutes for it to be running
239 while c.status != 'running' and n < 10:
243 if c.status != 'running' and n >= 10:
244 print('Container took to long to start!')
248 except docker.errors.ImageNotFound as ex:
249 # If the specified image does not exist.
253 except docker.errors.APIError as ex:
254 # If the server returns an error.
259 print('other error!')