def find_vm_in_vapp(ctx, vm_name=None, vm_id=None): result = [] try: resource_type = 'vApp' query = ctx.client.get_typed_query( resource_type, query_result_format=QueryResultFormat.ID_RECORDS) records = list(query.execute()) vdc_resource = ctx.vdc.get_resource() vdc_id = vdc_resource.get('id') vdc_name = vdc_resource.get('name') for curr_vapp in records: vapp_vdc = curr_vapp.get('vdc') if vdc_id != vapp_vdc: continue vapp_id = curr_vapp.get('id') vapp_name = curr_vapp.get('name') vapp_href = curr_vapp.get('href') the_vapp = ctx.vdc.get_vapp(vapp_name) for vm in the_vapp.Children.Vm: if vm.get('name') == vm_name or \ extract_id(vm.get('id')) == vm_id: result.append({ 'vdc': extract_id(vapp_vdc), 'vdc_name': vdc_name, 'vapp': extract_id(vapp_id), 'vapp_name': vapp_name, 'vm': extract_id(vm.get('id')), 'vm_name': vm.get('name'), 'vm_href': vm.get('href'), 'status': VCLOUD_STATUS_MAP.get(int(vm.get('status'))) }) break # Refresh session after Typed Query Client.login(session_id=ctx.token) except Exception as e: if ctx.config['debug'] == True: raise else: pass return result
def isattached(ctx, params, nodename): attached = False params = json.loads(params) try: is_logged_in = Client.login() if is_logged_in == False: raise Exception("Could not login to vCloud Director") vm = VApp.find_vm_in_vapp(Client.ctx, vm_name=nodename) if len(vm) > 0: vm = vm[0]['vm'] volume = params['volumeName'] disks = Disk.get_disks(Client.ctx) for disk in disks: if disk['name'] == volume \ and disk['attached_vm'] == vm: attached = True break success = {"status": "Success", "attached": attached} info(success) else: raise Exception(("Could not find node '%s'") % (nodename)) except Exception as e: failure = { "status": "Failure", "message": (("Error on line %d in file %s (%s): %s") % (sys.exc_info()[-1].tb_lineno, sys.exc_info()[-1].tb_frame.f_code.co_filename, type(e).__name__, e)) } error(failure) finally: Client.logout()
def delete(ctx, volume): config = Client.ctx.config try: is_logged_in = Client.login() if is_logged_in == False: raise Exception("Could not login to vCloud Director") disk_urn, attached_vm = Disk.find_disk(Client.ctx, volume) if disk_urn is None: raise Exception(("Volume '%s' does not exist") % (volume)) if attached_vm is not None: raise Exception( ("Could not delete attached volume '%s'") % (volume)) is_disk_deleted = Disk.delete_disk(Client.ctx, volume) if not is_disk_deleted: raise Exception(("Could not delete volume '%s'") % (volume)) info(GENERIC_SUCCESS) except Exception as e: failure = { "status": "Failure", "message": (("Error on line %d in file %s (%s): %s") % (sys.exc_info()[-1].tb_lineno, sys.exc_info()[-1].tb_frame.f_code.co_filename, type(e).__name__, e)) } error(failure) finally: Client.logout()
def waitforattach(ctx, mountdev, params): params = json.loads(params) try: is_logged_in = Client.login() if is_logged_in == False: raise Exception("Could not login to vCloud Director") volume = params['volumeName'] disk_urn, attached_vm = Disk.find_disk(Client.ctx, volume) if disk_urn is None: raise Exception(("Volume '%s' does not exist") % (volume)) volume_symlink = ("/dev/block/%s") % (disk_urn) partitions = [] if os.path.lexists(volume_symlink): device_name = "/dev/block/" + os.readlink(volume_symlink) try: mode = os.stat(device_name).st_mode assert stat.S_ISBLK(mode) == True except OSError: raise Exception(("Device '%s' does not exist") % (device_name)) except AssertionError: raise Exception( ("Device '%s' exists but is not a block device") % \ (device_name) ) partitions = disk_partitions(device_name.split('/')[-1]) attached = False for part in partitions: cmd_find_symlink = ( "find -L /dev/disk/by-path -samefile /dev/%s") % (part) try: ret = subprocess.check_output(cmd_find_symlink, shell=True) device = ret.decode().strip() if device: attached = True break except subprocess.CalledProcessError: continue if attached: success = {"status": "Success", "device": "%s" % device} info(success) else: raise Exception( ("Volume '%s' is not attached on the remote node") % \ (volume) ) except Exception as e: failure = { "status": "Failure", "message": (("Error on line %d in file %s (%s): %s") % (sys.exc_info()[-1].tb_lineno, sys.exc_info()[-1].tb_frame.f_code.co_filename, type(e).__name__, e)) } error(failure) finally: Client.logout()
def get_disks(ctx): result = [] attached_vm = \ lambda x, disk: next((i['vm'] for i in x if i['disk'] == disk), None) try: disks = ctx.vdc.get_disks() disks_relation = get_vm_disk_relation(ctx) for disk in disks: disk_id = extract_id(disk.get('id')) result.append({ 'name': disk.get('name'), 'id': disk_id, 'href': disk.get('href'), 'bus_type': int(disk.get('busType')), 'bus_sub_type': disk.get('busSubType'), 'size_bytes': int(disk.get('size')), 'size_human': bytes_to_size(int(disk.get('size'))), 'status': VCLOUD_STATUS_MAP.get(int(disk.get('status'))), 'attached_vm': attached_vm(disks_relation, disk_id), 'vdc': extract_id(ctx.vdc.resource.get('id')) }) # Refresh session after Typed Query Client.login(session_id=ctx.token) except Exception as e: if ctx.config['debug'] == True: raise else: pass return result
def detach(ctx, volume, nodename): config = Client.ctx.config try: is_disk_disconnected = [] is_logged_in = Client.login() if is_logged_in == False: raise Exception("Could not login to vCloud Director") disk_urn, attached_vm = Disk.find_disk2(Client.ctx, volume) if disk_urn is None: info(GENERIC_SUCCESS) volume_symlink = ("block/%s") % (disk_urn) volume_symlink_full = ("/dev/%s") % (volume_symlink) if os.path.lexists(volume_symlink_full): device_name = os.readlink(volume_symlink_full) device_name_short = device_name.split('/')[-1] if attached_vm is None: info(GENERIC_SUCCESS) else: vm = VApp.find_vm_in_vapp(Client.ctx, vm_id=attached_vm) # Check if attached to any node but not to current one vm_name = vm[0]['vm_name'] # If not return GENERIC_SUCCESS if vm_name != nodename: info(GENERIC_SUCCESS) # Process normal detach etcd = Etcd3Autodiscover(host=config['etcd']['host'], port=config['etcd']['port'], ca_cert=config['etcd']['ca_cert'], cert_key=config['etcd']['key'], cert_cert=config['etcd']['cert'], timeout=config['etcd']['timeout']) client = etcd.connect() if client is None: raise Exception( ("Could not connect to etcd server '%s'") % (etcd.errstr())) lock_name = ("vcloud/%s/disk/detach") % (nodename) lock_ttl = 120 with client.lock(lock_name, ttl=lock_ttl) as lock: n = 0 absolute = 10 while lock.is_acquired() == False and n < 6: timeout = round(Decimal(4 * 1.29**n)) absolute += timeout n += 1 lock.acquire(timeout=timeout) if lock.is_acquired() == False: raise Exception( ("Could not acquire lock after %0.fs. Giving up") % (absolute)) lock.refresh() is_disk_detached = Disk.detach_disk(Client.ctx, nodename, volume) if is_disk_detached == False: raise Exception( ("Could not detach volume '%s' from node '%s'") % \ (volume, nodename) ) else: is_disk_disconnected = wait_for_connected_disk(60) if len(is_disk_disconnected) == 0: raise DiskTimeoutException( ("Timed out while waiting for volume '%s' to detach from node '%s'") % \ (volume, nodename) ) # Make sure task is completed task = Client.ctx.client.get_task_monitor().wait_for_status( task=is_disk_detached, timeout=300, poll_frequency=2, fail_on_statuses=None, expected_target_statuses=[ TaskStatus.SUCCESS, TaskStatus.ABORTED, TaskStatus.ERROR, TaskStatus.CANCELED ], callback=None) assert task.get('status') == TaskStatus.SUCCESS.value lock.release() info(GENERIC_SUCCESS) except Exception as e: failure = { "status": "Failure", "message": (("Error on line %d in file %s (%s): %s") % (sys.exc_info()[-1].tb_lineno, sys.exc_info()[-1].tb_frame.f_code.co_filename, type(e).__name__, e)) } error(failure) finally: if len(is_disk_disconnected) == 2: device_status = is_disk_disconnected[1] if device_status == 'disconnected': if os.path.lexists(volume_symlink_full): os.unlink(volume_symlink_full) udev_rule_path = ( "/etc/udev/rules.d/90-vcloud-idisk-%s.rules") % (disk_urn) if os.path.exists(udev_rule_path): os.unlink(udev_rule_path) Client.logout()
def attach(ctx, params, nodename): params = json.loads(params) config = Client.ctx.config try: is_logged_in = Client.login() if is_logged_in == False: raise Exception("Could not login to vCloud Director") volume = params['volumeName'] disk_storage = params['storage'] if 'storage' in params else config[ 'default_storage'] disk_bus_type = params['busType'] if 'busType' in params else None disk_bus_sub_type = params[ 'busSubType'] if 'busSubType' in params else None disk_urn, attached_vm = Disk.find_disk(Client.ctx, volume) if disk_urn is None: disk_urn = Disk.create_disk(Client.ctx, volume, params['size'], disk_storage, bus_type=disk_bus_type, bus_sub_type=disk_bus_sub_type) if disk_urn == "": raise Exception(("Could not create volume '%s'") % (volume)) volume_symlink = ("block/%s") % (disk_urn) volume_symlink_full = ("/dev/%s") % (volume_symlink) if attached_vm: # Disk is in attached state vm = VApp.find_vm_in_vapp(Client.ctx, vm_id=attached_vm) # Check if attached to current node if len(vm) > 0: vm_name = vm[0]['vm_name'] if vm_name != nodename: # When node is marked unschedulable 'attach' command on a new node is called before 'detach' on the old one. # We poll volume for change attached_vm to None for total time 60s before we try to detach it. n = 0 while n < 6: timeout = round(Decimal(4 * 1.29**n)) n += 1 sleep(timeout) disk_urn, attached_vm = Disk.find_disk( Client.ctx, volume) if attached_vm is None: break if attached_vm: is_disk_detached = Disk.detach_disk(Client.ctx, vm_name, volume, block=True) if is_disk_detached == False: raise Exception( ("Could not detach volume '%s' from '%s'") % (volume, vm_name)) attached_vm = None else: raise Exception( ("Could not find attached VM '%s'. Does the VM exist?") % (attached_vm)) if attached_vm is None: etcd = Etcd3Autodiscover(host=config['etcd']['host'], port=config['etcd']['port'], ca_cert=config['etcd']['ca_cert'], cert_key=config['etcd']['key'], cert_cert=config['etcd']['cert'], timeout=config['etcd']['timeout']) client = etcd.connect() if client is None: raise Exception(("Could not connect to etcd server '%s'") % (etcd.errstr())) lock_name = ("vcloud/%s/disk/attach") % (nodename) lock_ttl = 120 with client.lock(lock_name, ttl=lock_ttl) as lock: n = 0 absolute = 10 while lock.is_acquired() == False and n < 6: timeout = round(Decimal(4 * 1.29**n)) absolute += timeout n += 1 lock.acquire(timeout=timeout) if lock.is_acquired() == False: raise Exception( ("Could not acquire lock after %0.fs. Giving up") % (absolute)) lock.refresh() is_disk_attached = Disk.attach_disk(Client.ctx, nodename, volume) if is_disk_attached == False: raise Exception( ("Could not attach volume '%s' to node '%s'") % (volume, nodename)) is_disk_connected = wait_for_connected_disk(60) if len(is_disk_connected) == 0: raise DiskTimeoutException( ("Timed out while waiting for volume '%s' to attach to node '%s'") % \ (volume, nodename) ) # Make sure task is completed task = Client.ctx.client.get_task_monitor().wait_for_status( task=is_disk_attached, timeout=60, poll_frequency=2, fail_on_statuses=None, expected_target_statuses=[ TaskStatus.SUCCESS, TaskStatus.ABORTED, TaskStatus.ERROR, TaskStatus.CANCELED ], callback=None) # Sometimes task "fails" with error: # majorErrorCode=500 and message=Unable to perform this action. assert task.get('status') == TaskStatus.SUCCESS.value device_name, device_status = is_disk_connected device_name_short = device_name.split('/')[-1] disk_path = get_disk_path(device_name) disk_path_short = disk_path.split('/')[-1] if os.path.lexists(volume_symlink_full) == False: os.symlink("../" + device_name_short, volume_symlink_full) # Create udev rule to fix: https://github.com/sysoperator/kube-vcloud-flexvolume/issues/7 # Use more stable device names: # SUBSYSTEM=="block", ENV{ID_TYPE}=="disk", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="pci-0000:03:00.0-scsi-0:0:1:0", SYMLINK+="block/7e9554ee-0bca-43b8-80a0-c50498ba45b1" udev_rule_path = ( "/etc/udev/rules.d/90-vcloud-idisk-%s.rules") % ( disk_urn) with open(udev_rule_path, "w") as udev_rule: udev_rule.write( ('SUBSYSTEM=="block", ENV{ID_TYPE}=="disk", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="%s", SYMLINK+="%s"\n') % \ (disk_path_short, volume_symlink) ) udev_rule.close() lock.release() else: if os.path.lexists(volume_symlink_full): device_name = "/dev/block/" + os.readlink(volume_symlink_full) device_name_short = device_name.split('/')[-1] try: mode = os.stat(device_name).st_mode assert stat.S_ISBLK(mode) == True except OSError: raise Exception( ("Device '%s' does not exist on node '%s'") % (device_name, nodename)) except AssertionError: raise Exception( ("Device '%s' exists on node '%s' but is not a block device") % \ (device_name, nodename) ) else: import inspect raise Exception( ("Fatal error on line %d. This should never happen") % (inspect.currentframe().f_lineno)) partitions = disk_partitions(device_name_short) if len(partitions) == 0: try: # See: http://man7.org/linux/man-pages/man8/sfdisk.8.html # Fixed: https://github.com/answear/kube-vcloud-flexvolume/issues/18 cmd_create_partition = ("echo ',,83;' | sfdisk %s") % ( device_name) subprocess.check_call(cmd_create_partition, shell=True, stdout=DEVNULL, stderr=DEVNULL) partition = ("%s%d") % (device_name, 1) except subprocess.CalledProcessError: raise Exception(("Could not create partition on device '%s'") % (device_name)) else: partitions.sort() partition = ("/%s/%s") % (device_name.split('/')[1], partitions[0]) cmd_find_symlink = ("find -L /dev/disk/by-path -samefile %s") % ( partition) found = False try: # with timeout _timeout = 5 _start = time() while time() < _start + _timeout: sleep(1) ret = subprocess.check_output(cmd_find_symlink, shell=True) partition = ret.decode().strip() if partition.startswith("/dev/disk/by-path"): found = True break except subprocess.CalledProcessError: pass if found == False: raise Exception(( "Could not find symlink for partition '%s' in /dev/disk/by-path dir" ) % (partition)) success = {"status": "Success", "device": "%s" % partition} info(success) except Exception as e: failure = { "status": "Failure", "message": (("Error on line %d in file %s (%s): %s") % (sys.exc_info()[-1].tb_lineno, sys.exc_info()[-1].tb_frame.f_code.co_filename, type(e).__name__, e)) } error(failure) finally: Client.logout()