CAL refactoring
[osm/SO.git] / rwcal / plugins / vala / rwcal_openstack / rift / rwcal / openstack / prepare_vm.py
index 7acf0fd..7f3800b 100644 (file)
@@ -21,6 +21,10 @@ import logging
 import argparse
 import sys, os, time
 import rwlogger
+import yaml
+import random
+import fcntl
+
 
 logging.basicConfig(level=logging.DEBUG)
 logger = logging.getLogger()
@@ -30,29 +34,93 @@ rwlog_handler = rwlogger.RwLogger(category="rw-cal-log",
 logger.addHandler(rwlog_handler)
 #logger.setLevel(logging.DEBUG)
 
+class FileLock:
+    FILE_LOCK = '/tmp/_openstack_prepare_vm.lock'
+    def __init__(self):
+        # This will create it if it does not exist already
+        self.filename = FileLock.FILE_LOCK
+        self.handle = None
+        
+    # Bitwise OR fcntl.LOCK_NB if you need a non-blocking lock
+    def acquire(self):
+        logger.info("<PID: %d> Attempting to acquire log." %os.getpid())
+        self.handle = open(self.filename, 'w')
+        fcntl.flock(self.handle, fcntl.LOCK_EX)
+        logger.info("<PID: %d> Lock successfully acquired." %os.getpid())
+        
+    def release(self):
+        fcntl.flock(self.handle, fcntl.LOCK_UN)
+        self.handle.close()
+        logger.info("<PID: %d> Released lock." %os.getpid())
+        
+    def __del__(self):
+        if self.handle and self.handle.closed == False:
+            self.handle.close()
+                                                                                        
+    
+def allocate_floating_ip(drv, argument):
+    #### Allocate a floating_ip
+    available_ip = [ ip for ip in drv.nova_floating_ip_list() if ip.instance_id == None ]
+
+    if argument.pool_name:
+        ### Filter further based on IP address
+        available_ip = [ ip for ip in available_ip if ip.pool == argument.pool_name ]
+        
+    if not available_ip:
+        logger.info("<PID: %d> No free floating_ips available. Allocating fresh from pool: %s" %(os.getpid(), argument.pool_name))
+        pool_name = argument.pool_name if argument.pool_name is not None else None
+        floating_ip = drv.nova_floating_ip_create(pool_name)
+    else:
+        floating_ip = random.choice(available_ip)
+        logger.info("<PID: %d> Selected floating_ip: %s from available free pool" %(os.getpid(), floating_ip))
+
+    return floating_ip
 
+
+def handle_floating_ip_assignment(drv, server, argument, management_ip):
+    lock = FileLock()
+    ### Try 3 time (<<<magic number>>>)
+    RETRY = 3
+    for attempt in range(RETRY):
+        try:
+            lock.acquire()
+            floating_ip = allocate_floating_ip(drv, argument)
+            logger.info("Assigning the floating_ip: %s to VM: %s" %(floating_ip, server['name']))
+            drv.nova_floating_ip_assign(argument.server_id,
+                                        floating_ip,
+                                        management_ip)
+            logger.info("Assigned floating_ip: %s to management_ip: %s" %(floating_ip, management_ip))
+        except Exception as e:
+            logger.error("Could not assign floating_ip: %s to VM: %s. Exception: %s" %(floating_ip, server['name'], str(e)))
+            lock.release()
+            if attempt == (RETRY -1):
+                logger.error("Max attempts %d reached for floating_ip allocation. Giving up" %attempt)
+                raise
+            else:
+                logger.error("Retrying floating ip allocation. Current retry count: %d" %attempt)
+        else:
+            lock.release()
+            return
+    
+        
 def assign_floating_ip_address(drv, argument):
     if not argument.floating_ip:
         return
 
     server = drv.nova_server_get(argument.server_id)
-    logger.info("Assigning the floating_ip: %s to VM: %s" %(argument.floating_ip, server['name']))
-    
+
     for i in range(120):
         server = drv.nova_server_get(argument.server_id)
         for network_name,network_info in server['addresses'].items():
-            if network_info:
-                if network_name == argument.mgmt_network:
-                    for n_info in network_info:
-                        if 'OS-EXT-IPS:type' in n_info and n_info['OS-EXT-IPS:type'] == 'fixed':
-                            management_ip = n_info['addr']
-                            drv.nova_floating_ip_assign(argument.server_id,
-                                                        argument.floating_ip,
-                                                        management_ip)
-                            logger.info("Assigned floating_ip: %s to management_ip: %s" %(argument.floating_ip, management_ip))
+            if network_info and  network_name == argument.mgmt_network:
+                for n_info in network_info:
+                    if 'OS-EXT-IPS:type' in n_info and n_info['OS-EXT-IPS:type'] == 'fixed':
+                        management_ip = n_info['addr']
+                        handle_floating_ip_assignment(drv, server, argument, management_ip)
                         return
-        logger.info("Waiting for management_ip to be assigned to server: %s" %(server['name']))
-        time.sleep(1)
+        else:
+            logger.info("Waiting for management_ip to be assigned to server: %s" %(server['name']))
+            time.sleep(1)
     else:
         logger.info("No management_ip IP available to associate floating_ip for server: %s" %(server['name']))
     return
@@ -90,15 +158,52 @@ def create_port_metadata(drv, argument):
         
     nvconn = drv.nova_drv._get_nova_connection()
     nvconn.servers.set_meta(argument.server_id, meta_data)
+
+def get_volume_id(server_vol_list, name):
+    if server_vol_list is None:
+        return
+
+    for os_volume in server_vol_list:
+        try:
+            " Device name is of format /dev/vda"
+            vol_name = (os_volume['device']).split('/')[2]
+        except:                   
+            continue
+        if name == vol_name:
+           return os_volume['volumeId']
     
+def create_volume_metadata(drv, argument):
+    if argument.vol_metadata is None:
+        return
+
+    yaml_vol_str = argument.vol_metadata.read()
+    yaml_vol_cfg = yaml.load(yaml_vol_str)
+
+    srv_volume_list = drv.nova_volume_list(argument.server_id)
+    for volume in yaml_vol_cfg:
+        if 'custom_meta_data' not in volume:
+            continue
+        vmd = dict()
+        for vol_md_item in volume['custom_meta_data']:
+            if 'value' not in vol_md_item:
+               continue
+            vmd[vol_md_item['name']] = vol_md_item['value']
+
+        # Get volume id
+        vol_id = get_volume_id(srv_volume_list, volume['name'])
+        if vol_id is None:
+            logger.error("Server %s Could not find volume %s" %(argument.server_id, volume['name']))
+            sys.exit(3)
+        drv.cinder_volume_set_metadata(vol_id, vmd)
+
         
 def prepare_vm_after_boot(drv,argument):
     ### Important to call create_port_metadata before assign_floating_ip_address
     ### since assign_floating_ip_address can wait thus delaying port_metadata creation
 
-    ### Wait for 2 minute for server to come up -- Needs fine tuning
-    wait_time = 120 
-    sleep_time = 1
+    ### Wait for a max of 5 minute for server to come up -- Needs fine tuning
+    wait_time = 500
+    sleep_time = 2
     for i in range(int(wait_time/sleep_time)):
         server = drv.nova_server_get(argument.server_id)
         if server['status'] == 'ACTIVE':
@@ -115,6 +220,7 @@ def prepare_vm_after_boot(drv,argument):
         sys.exit(4)
     
     #create_port_metadata(drv, argument)
+    create_volume_metadata(drv, argument)
     assign_floating_ip_address(drv, argument)
     
 
@@ -147,6 +253,27 @@ def main():
                         type = str,
                         help = "Tenant name openstack installation")
 
+    parser.add_argument('--user_domain',
+                        action = "store",
+                        dest = "user_domain",
+                        default = None,
+                        type = str,
+                        help = "User domain name for openstack installation")
+
+    parser.add_argument('--project_domain',
+                        action = "store",
+                        dest = "project_domain",
+                        default = None,
+                        type = str,
+                        help = "Project domain name for openstack installation")
+
+    parser.add_argument('--region',
+                        action = "store",
+                        dest = "region",
+                        default = "RegionOne",
+                        type = str,
+                        help = "Region name for openstack installation")
+    
     parser.add_argument('--mgmt_network',
                         action = "store",
                         dest = "mgmt_network",
@@ -160,10 +287,17 @@ def main():
                         help = "Server ID on which boot operations needs to be performed")
     
     parser.add_argument('--floating_ip',
-                        action = "store",
+                        action = "store_true",
                         dest = "floating_ip",
+                        default = False,
+                        help = "Floating IP assignment required")
+
+    parser.add_argument('--pool_name',
+                        action = "store",
+                        dest = "pool_name",
                         type = str,
-                        help = "Floating IP to be assigned")
+                        help = "Floating IP pool name")
+
 
     parser.add_argument('--port_metadata',
                         action = "store_true",
@@ -171,6 +305,8 @@ def main():
                         default = False,
                         help = "Create Port Metadata")
 
+    parser.add_argument("--vol_metadata", type=argparse.FileType('r'))
+
     argument = parser.parse_args()
 
     if not argument.auth_url:
@@ -209,7 +345,6 @@ def main():
     else:
         logger.info("Using Server ID : %s" %(argument.server_id))
         
-        
     try:
         pid = os.fork()
         if pid > 0:
@@ -218,12 +353,18 @@ def main():
     except OSError as e:
         logger.error("fork failed: %d (%s)\n" % (e.errno, e.strerror))
         sys.exit(2)
-        
-    drv = openstack_drv.OpenstackDriver(username = argument.username,
-                                        password = argument.password,
-                                        auth_url = argument.auth_url,
-                                        tenant_name = argument.tenant_name,
-                                        mgmt_network = argument.mgmt_network)
+
+    kwargs = dict(username = argument.username,
+                  password = argument.password,
+                  auth_url = argument.auth_url,
+                  project =  argument.tenant_name,
+                  mgmt_network = argument.mgmt_network,
+                  cert_validate = False,
+                  user_domain = argument.user_domain,
+                  project_domain = argument.project_domain,
+                  region = argument.region)
+
+    drv = openstack_drv.OpenstackDriver(logger = logger, **kwargs)
     prepare_vm_after_boot(drv, argument)
     sys.exit(0)