Revert "Full Juju Charm support"
[osm/SO.git] / rwcal / plugins / vala / rwcal_openstack / rift / rwcal / openstack / prepare_vm.py
1 #!/usr/bin/env python3
2
3 #
4 # Copyright 2016 RIFT.IO Inc
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18
19 import rift.rwcal.openstack as openstack_drv
20 import logging
21 import argparse
22 import sys, os, time
23 import rwlogger
24 import yaml
25 import random
26 import fcntl
27
28
29 logging.basicConfig(level=logging.DEBUG)
30 logger = logging.getLogger()
31 rwlog_handler = rwlogger.RwLogger(category="rw-cal-log",
32 subcategory="openstack",)
33 logger.addHandler(rwlog_handler)
34 #logger.setLevel(logging.DEBUG)
35
36 class FileLock:
37 FILE_LOCK = '/tmp/_openstack_prepare_vm.lock'
38 def __init__(self):
39 # This will create it if it does not exist already
40 self.filename = FileLock.FILE_LOCK
41 self.handle = None
42
43 # Bitwise OR fcntl.LOCK_NB if you need a non-blocking lock
44 def acquire(self):
45 logger.info("<PID: %d> Attempting to acquire log." %os.getpid())
46 self.handle = open(self.filename, 'w')
47 fcntl.flock(self.handle, fcntl.LOCK_EX)
48 logger.info("<PID: %d> Lock successfully acquired." %os.getpid())
49
50 def release(self):
51 fcntl.flock(self.handle, fcntl.LOCK_UN)
52 self.handle.close()
53 logger.info("<PID: %d> Released lock." %os.getpid())
54
55 def __del__(self):
56 if self.handle and self.handle.closed == False:
57 self.handle.close()
58
59
60 def allocate_floating_ip(drv, argument):
61 #### Allocate a floating_ip
62 try:
63 available_ip = [ ip for ip in drv.nova_floating_ip_list() if ip.instance_id == None ]
64
65 if argument.pool_name:
66 ### Filter further based on IP address
67 available_ip = [ ip for ip in available_ip if ip.pool == argument.pool_name ]
68
69 if not available_ip:
70 logger.info("<PID: %d> No free floating_ips available. Allocating fresh from pool: %s" %(os.getpid(), argument.pool_name))
71 pool_name = argument.pool_name if argument.pool_name is not None else None
72 floating_ip = drv.nova_floating_ip_create(pool_name)
73 else:
74 floating_ip = random.choice(available_ip)
75 logger.info("<PID: %d> Selected floating_ip: %s from available free pool" %(os.getpid(), floating_ip))
76
77 return floating_ip
78
79 except Exception as e:
80 logger.error("Floating IP Allocation Failed - %s", e)
81 return None
82
83
84 def handle_floating_ip_assignment(drv, server, argument, management_ip):
85 lock = FileLock()
86 ### Try 3 time (<<<magic number>>>)
87 RETRY = 3
88 for attempt in range(RETRY):
89 try:
90 lock.acquire()
91 floating_ip = allocate_floating_ip(drv, argument)
92 logger.info("Assigning the floating_ip: %s to VM: %s" %(floating_ip, server['name']))
93 drv.nova_floating_ip_assign(argument.server_id,
94 floating_ip,
95 management_ip)
96 logger.info("Assigned floating_ip: %s to management_ip: %s" %(floating_ip, management_ip))
97 except Exception as e:
98 logger.error("Could not assign floating_ip: %s to VM: %s. Exception: %s" %(floating_ip, server['name'], str(e)))
99 lock.release()
100 if attempt == (RETRY -1):
101 logger.error("Max attempts %d reached for floating_ip allocation. Giving up" %attempt)
102 raise
103 else:
104 logger.error("Retrying floating ip allocation. Current retry count: %d" %attempt)
105 else:
106 lock.release()
107 return
108
109
110 def assign_floating_ip_address(drv, argument):
111 if not argument.floating_ip:
112 return
113
114 server = drv.nova_server_get(argument.server_id)
115
116 for i in range(120):
117 server = drv.nova_server_get(argument.server_id)
118 for network_name,network_info in server['addresses'].items():
119 if network_info and network_name == argument.mgmt_network:
120 for n_info in network_info:
121 if 'OS-EXT-IPS:type' in n_info and n_info['OS-EXT-IPS:type'] == 'fixed':
122 management_ip = n_info['addr']
123 try:
124 handle_floating_ip_assignment(drv, server, argument, management_ip)
125 return
126 except Exception as e:
127 logger.error("Exception in assign_floating_ip_address : %s", e)
128 raise
129 else:
130 logger.info("Waiting for management_ip to be assigned to server: %s" %(server['name']))
131 time.sleep(1)
132 else:
133 logger.info("No management_ip IP available to associate floating_ip for server: %s" %(server['name']))
134 return
135
136
137 def create_port_metadata(drv, argument):
138 if argument.port_metadata == False:
139 return
140
141 ### Get Management Network ID
142 network_list = drv.neutron_network_list()
143 mgmt_network_id = [net['id'] for net in network_list if net['name'] == argument.mgmt_network][0]
144 port_list = [ port for port in drv.neutron_port_list(**{'device_id': argument.server_id})
145 if port['network_id'] != mgmt_network_id ]
146 meta_data = {}
147
148 meta_data['rift-meta-ports'] = str(len(port_list))
149 port_id = 0
150 for port in port_list:
151 info = []
152 info.append('"port_name":"'+port['name']+'"')
153 if 'mac_address' in port:
154 info.append('"hw_addr":"'+port['mac_address']+'"')
155 if 'network_id' in port:
156 #info.append('"network_id":"'+port['network_id']+'"')
157 net_name = [net['name'] for net in network_list if net['id'] == port['network_id']]
158 if net_name:
159 info.append('"network_name":"'+net_name[0]+'"')
160 if 'fixed_ips' in port:
161 ip_address = port['fixed_ips'][0]['ip_address']
162 info.append('"ip":"'+ip_address+'"')
163
164 meta_data['rift-meta-port-'+str(port_id)] = '{' + ','.join(info) + '}'
165 port_id += 1
166
167 nvconn = drv.nova_drv._get_nova_connection()
168 nvconn.servers.set_meta(argument.server_id, meta_data)
169
170 def get_volume_id(server_vol_list, name):
171 if server_vol_list is None:
172 return
173
174 for os_volume in server_vol_list:
175 try:
176 " Device name is of format /dev/vda"
177 vol_name = (os_volume['device']).split('/')[2]
178 except:
179 continue
180 if name == vol_name:
181 return os_volume['volumeId']
182
183 def create_volume_metadata(drv, argument):
184 if argument.vol_metadata is None:
185 return
186
187 yaml_vol_str = argument.vol_metadata.read()
188 yaml_vol_cfg = yaml.load(yaml_vol_str)
189
190 srv_volume_list = drv.nova_volume_list(argument.server_id)
191 for volume in yaml_vol_cfg:
192 if 'custom_meta_data' not in volume:
193 continue
194 vmd = dict()
195 for vol_md_item in volume['custom_meta_data']:
196 if 'value' not in vol_md_item:
197 continue
198 vmd[vol_md_item['name']] = vol_md_item['value']
199
200 # Get volume id
201 vol_id = get_volume_id(srv_volume_list, volume['name'])
202 if vol_id is None:
203 logger.error("Server %s Could not find volume %s" %(argument.server_id, volume['name']))
204 sys.exit(3)
205 drv.cinder_volume_set_metadata(vol_id, vmd)
206
207
208 def prepare_vm_after_boot(drv,argument):
209 ### Important to call create_port_metadata before assign_floating_ip_address
210 ### since assign_floating_ip_address can wait thus delaying port_metadata creation
211
212 ### Wait for a max of 5 minute for server to come up -- Needs fine tuning
213 wait_time = 500
214 sleep_time = 2
215 for i in range(int(wait_time/sleep_time)):
216 server = drv.nova_server_get(argument.server_id)
217 if server['status'] == 'ACTIVE':
218 logger.info("Server %s to reached active state" %(server['name']))
219 break
220 elif server['status'] == 'BUILD':
221 logger.info("Waiting for server: %s to build. Current state: %s" %(server['name'], server['status']))
222 time.sleep(sleep_time)
223 else:
224 logger.info("Server %s reached state: %s" %(server['name'], server['status']))
225 sys.exit(3)
226 else:
227 logger.error("Server %s did not reach active state in %d seconds. Current state: %s" %(server['name'], wait_time, server['status']))
228 sys.exit(4)
229 #create_port_metadata(drv, argument)
230 create_volume_metadata(drv, argument)
231 try:
232 assign_floating_ip_address(drv, argument)
233 except Exception as e:
234 logger.error("Exception in prepare_vm_after_boot : %s", e)
235 raise
236
237
238 def main():
239 """
240 Main routine
241 """
242 parser = argparse.ArgumentParser(description='Script to create openstack resources')
243 parser.add_argument('--auth_url',
244 action = "store",
245 dest = "auth_url",
246 type = str,
247 help='Keystone Auth URL')
248
249 parser.add_argument('--username',
250 action = "store",
251 dest = "username",
252 type = str,
253 help = "Username for openstack installation")
254
255 parser.add_argument('--password',
256 action = "store",
257 dest = "password",
258 type = str,
259 help = "Password for openstack installation")
260
261 parser.add_argument('--tenant_name',
262 action = "store",
263 dest = "tenant_name",
264 type = str,
265 help = "Tenant name openstack installation")
266
267 parser.add_argument('--user_domain',
268 action = "store",
269 dest = "user_domain",
270 default = None,
271 type = str,
272 help = "User domain name for openstack installation")
273
274 parser.add_argument('--project_domain',
275 action = "store",
276 dest = "project_domain",
277 default = None,
278 type = str,
279 help = "Project domain name for openstack installation")
280
281 parser.add_argument('--region',
282 action = "store",
283 dest = "region",
284 default = "RegionOne",
285 type = str,
286 help = "Region name for openstack installation")
287
288 parser.add_argument('--mgmt_network',
289 action = "store",
290 dest = "mgmt_network",
291 type = str,
292 help = "mgmt_network")
293
294 parser.add_argument('--server_id',
295 action = "store",
296 dest = "server_id",
297 type = str,
298 help = "Server ID on which boot operations needs to be performed")
299
300 parser.add_argument('--floating_ip',
301 action = "store_true",
302 dest = "floating_ip",
303 default = False,
304 help = "Floating IP assignment required")
305
306 parser.add_argument('--pool_name',
307 action = "store",
308 dest = "pool_name",
309 type = str,
310 help = "Floating IP pool name")
311
312
313 parser.add_argument('--port_metadata',
314 action = "store_true",
315 dest = "port_metadata",
316 default = False,
317 help = "Create Port Metadata")
318
319 parser.add_argument("--vol_metadata", type=argparse.FileType('r'))
320
321 argument = parser.parse_args()
322
323 if not argument.auth_url:
324 logger.error("ERROR: AuthURL is not configured")
325 sys.exit(1)
326 else:
327 logger.info("Using AuthURL: %s" %(argument.auth_url))
328
329 if not argument.username:
330 logger.error("ERROR: Username is not configured")
331 sys.exit(1)
332 else:
333 logger.info("Using Username: %s" %(argument.username))
334
335 if not argument.password:
336 logger.error("ERROR: Password is not configured")
337 sys.exit(1)
338 else:
339 logger.info("Using Password: %s" %(argument.password))
340
341 if not argument.tenant_name:
342 logger.error("ERROR: Tenant Name is not configured")
343 sys.exit(1)
344 else:
345 logger.info("Using Tenant Name: %s" %(argument.tenant_name))
346
347 if not argument.mgmt_network:
348 logger.error("ERROR: Management Network Name is not configured")
349 sys.exit(1)
350 else:
351 logger.info("Using Management Network: %s" %(argument.mgmt_network))
352
353 if not argument.server_id:
354 logger.error("ERROR: Server ID is not configured")
355 sys.exit(1)
356 else:
357 logger.info("Using Server ID : %s" %(argument.server_id))
358
359 try:
360 pid = os.fork()
361 if pid > 0:
362 # exit for parent
363 sys.exit(0)
364 except OSError as e:
365 logger.error("fork failed: %d (%s)\n" % (e.errno, e.strerror))
366 sys.exit(2)
367
368 kwargs = dict(username = argument.username,
369 password = argument.password,
370 auth_url = argument.auth_url,
371 project = argument.tenant_name,
372 mgmt_network = argument.mgmt_network,
373 cert_validate = False,
374 user_domain = argument.user_domain,
375 project_domain = argument.project_domain,
376 region = argument.region)
377
378 drv = openstack_drv.OpenstackDriver(logger = logger, **kwargs)
379 try:
380 prepare_vm_after_boot(drv, argument)
381 except Exception as e:
382 logger.error("Exception in main of prepare_vm : %s", e)
383 raise
384
385 if __name__ == "__main__":
386 try:
387 main()
388 # Do not print anything in this script. This is a subprocess spawned by rwmain
389 # and the following print determines the success or failure of this script.
390 print("True",end="")
391 except Exception as e:
392 logger.error("Exception in prepare_vm : %s", e)
393 # Do not print anything in this script. This is a subprocess spawned by rwmain
394 # and the following print determines the success or failure of this script.
395 print("False+" + str(e),end="")
396 sys.exit(2)
397
398