def _migrate_image(self, image_uuid, image_size, src_install_path, dst_install_path, dst_mon_addr, dst_mon_user, dst_mon_passwd, dst_mon_port): src_install_path = self._normalize_install_path(src_install_path) dst_install_path = self._normalize_install_path(dst_install_path) ssh_cmd, tmp_file = linux.build_sshpass_cmd( dst_mon_addr, dst_mon_passwd, 'tee >(md5sum >/tmp/%s_dst_md5) | rbd import - %s' % (image_uuid, dst_install_path), dst_mon_user, dst_mon_port) rst = shell.run( "rbd export %s - | tee >(md5sum >/tmp/%s_src_md5) | %s" % (src_install_path, image_uuid, ssh_cmd)) linux.rm_file_force(tmp_file) if rst != 0: return rst src_md5 = self._read_file_content('/tmp/%s_src_md5' % image_uuid) dst_md5 = linux.sshpass_call(dst_mon_addr, dst_mon_passwd, 'cat /tmp/%s_dst_md5' % image_uuid, dst_mon_user, dst_mon_port) if src_md5 != dst_md5: return -1 else: return 0
def ping(self, req): cmd = jsonobject.loads(req[http.REQUEST_BODY]) if cmd.uuid not in self.mount_path.keys(): self.mount_path[cmd.uuid] = cmd.mountPath mount_path = self.mount_path[cmd.uuid] # if nfs service stop, os.path.isdir will hung if not linux.timeout_isdir(mount_path) or not linux.is_mounted( path=mount_path): raise Exception( 'the mount path[%s] of the nfs primary storage[uuid:%s] is not existing' % (mount_path, cmd.uuid)) test_file = os.path.join(mount_path, '%s-ping-test-file' % uuidhelper.uuid()) touch = shell.ShellCmd('timeout 60 touch %s' % test_file) touch(False) if touch.return_code == 124: raise Exception( 'unable to access the mount path[%s] of the nfs primary storage[uuid:%s] in 60s, timeout' % (mount_path, cmd.uuid)) elif touch.return_code != 0: touch.raise_error() linux.rm_file_force(test_file) return jsonobject.dumps(NfsResponse())
def upload(self, req): imageUuid = req.headers['X-IMAGE-UUID'] imageSize = req.headers['X-IMAGE-SIZE'] task = self.upload_tasks.get_task(imageUuid) if task is None: raise Exception('image not found %s' % imageUuid) task.expectedSize = long(imageSize) total, avail, poolCapacities, xsky = self._get_capacity() if avail <= task.expectedSize: self._fail_task(task, 'capacity not enough for size: ' + imageSize) entity = req.body boundary = get_boundary(entity) if not boundary: self._fail_task(task, 'unexpected post form') try: # prepare the fifo to save image upload fpath = self._get_fifopath(imageUuid) linux.rm_file_force(fpath) os.mkfifo(fpath) stream_body(task, fpath, entity, boundary) except Exception as e: self._fail_task(task, str(e)) finally: linux.rm_file_force(fpath)
def check_other_smp_and_set_id_file(uuid, existUuids): o = shell.ShellCmd('''\ ls %s | grep -v %s | grep -o "[0-9a-f]\{8\}[0-9a-f]\{4\}[1-5][0-9a-f]\{3\}[89ab][0-9a-f]\{3\}[0-9a-f]\{12\}"\ ''' % (id_dir, uuid)) o(False) if o.return_code != 0: file_uuids = [] else: file_uuids = o.stdout.splitlines() for file_uuid in file_uuids: if file_uuid in existUuids: raise Exception( "the mount point [%s] has been occupied by other SMP[uuid:%s], Please attach this directly" % (cmd.mountPoint, file_uuid)) logger.debug("existing id files: %s" % file_uuids) self.id_files[uuid] = os.path.join(id_dir, uuid) if not os.path.exists(self.id_files[uuid]): # check if hosts in the same cluster mount the same path but different storages. rsp.isFirst = True for file_uuid in file_uuids: linux.rm_file_force(os.path.join(id_dir, file_uuid)) linux.touch_file(self.id_files[uuid]) linux.sync()
def check_other_smp_and_set_id_file(uuid, existUuids): o = shell.ShellCmd('''\ ls %s | grep -v %s | grep -o "[0-9a-f]\{8\}[0-9a-f]\{4\}[1-5][0-9a-f]\{3\}[89ab][0-9a-f]\{3\}[0-9a-f]\{12\}"\ ''' % (id_dir, uuid)) o(False) if o.return_code != 0: file_uuids = [] else: file_uuids = o.stdout.splitlines() for file_uuid in file_uuids: if file_uuid in existUuids: raise Exception( "the mount point [%s] has been occupied by other SMP[uuid:%s], Please attach this directly" % (cmd.mountPoint, file_uuid)) logger.debug("existing id files: %s" % file_uuids) self.id_files[uuid] = os.path.join(id_dir, uuid) if not os.path.exists(self.id_files[uuid]): # check if hosts in the same cluster mount the same path but different storages. rsp.isFirst = True for file_uuid in file_uuids: linux.rm_file_force(os.path.join(id_dir, file_uuid)) linux.touch_file(self.id_files[uuid]) linux.sync_file(self.id_files[uuid])
def upload(self, req): imageUuid = req.headers['X-IMAGE-UUID'] imageSize = req.headers['X-IMAGE-SIZE'] task = self.upload_tasks.get_task(imageUuid) if task is None: raise Exception('image not found %s' % imageUuid) task.expectedSize = long(imageSize) total, avail, poolCapacities, xsky = self._get_capacity() if avail <= task.expectedSize: self._fail_task(task, 'capacity not enough for size: ' + imageSize) entity = req.body boundary = get_boundary(entity) if not boundary: self._fail_task(task, 'unexpected post form') try: # prepare the fifo to save image upload fpath = self._get_fifopath(imageUuid) linux.rm_file_force(fpath) os.mkfifo(fpath) stream_body(task, fpath, entity, boundary) except Exception as e: self._fail_task(task, str(e)) finally: linux.rm_file_force(fpath)
def heartbeat_on_aliyunnas(): failure = 0 while self.run_fencer(cmd.uuid, created_time): try: time.sleep(cmd.interval) mount_path = cmd.mountPath test_file = os.path.join( mount_path, cmd.heartbeat, '%s-ping-test-file-%s' % (cmd.uuid, kvmagent.HOST_UUID)) touch = shell.ShellCmd('timeout 5 touch %s' % test_file) touch(False) if touch.return_code != 0: logger.debug('touch file failed, cause: %s' % touch.stderr) failure += 1 else: failure = 0 linux.rm_file_force(test_file) continue if failure < cmd.maxAttempts: continue try: logger.warn("aliyun nas storage %s fencer fired!" % cmd.uuid) if cmd.strategy == 'Permissive': continue vm_uuids = kill_vm(cmd.maxAttempts).keys() if vm_uuids: self.report_self_fencer_triggered( [cmd.uuid], ','.join(vm_uuids)) clean_network_config(vm_uuids) # reset the failure count failure = 0 except Exception as e: logger.warn("kill vm failed, %s" % e.message) content = traceback.format_exc() logger.warn("traceback: %s" % content) finally: self.report_storage_status([cmd.uuid], 'Disconnected') except Exception as e: logger.debug( 'self-fencer on aliyun nas primary storage %s stopped abnormally' % cmd.uuid) content = traceback.format_exc() logger.warn(content) logger.debug('stop self-fencer on aliyun nas primary storage %s' % cmd.uuid)
def cancel_download_from_kvmhost(self, req): cmd = jsonobject.loads(req[http.REQUEST_BODY]) rsp = kvmagent.AgentResponse() install_abs_path = cmd.primaryStorageInstallPath shell.run("pkill -9 -f '%s'" % install_abs_path) linux.rm_file_force(cmd.primaryStorageInstallPath) return jsonobject.dumps(rsp)
def _delete_target(self, target_name, conf_uuid): conf_file = os.path.join('/etc/tgt/conf.d/%s.conf' % conf_uuid) linux.rm_file_force(conf_file) output = shell.call('tgt-admin --show') if target_name not in output: return update_target(target_name)
def _delete_target(self, target_name, conf_uuid): conf_file = os.path.join('/etc/tgt/conf.d/%s.conf' % conf_uuid) linux.rm_file_force(conf_file) output = shell.call('tgt-admin --show') if target_name not in output: return update_target(target_name)
def deleteImage(path): linux.rm_file_checked(path) logger.debug('successfully delete %s' % path) if (path.endswith('.qcow2')): imfFiles = [".imf",".imf2"] for f in imfFiles: filePath = path.replace(".qcow2", f) linux.rm_file_force(filePath) pdir = os.path.dirname(path) linux.rmdir_if_empty(pdir)
def deleteImage(path): linux.rm_file_checked(path) logger.debug('successfully delete %s' % path) if (path.endswith('.qcow2')): imfFiles = [".imf", ".imf2"] for f in imfFiles: filePath = path.replace(".qcow2", f) linux.rm_file_force(filePath) pdir = os.path.dirname(path) linux.rmdir_if_empty(pdir)
def _kill_lb(self, to): pid_file_path = self._make_pid_file_path(to.lbUuid, to.listenerUuid) pid = linux.find_process_by_cmdline([pid_file_path]) if pid: shell.call('kill %s' % pid) linux.rm_file_force(pid_file_path) linux.rm_file_force(self._make_conf_file_path(to.lbUuid, to.listenerUuid)) ipt = iptables.from_iptables_save() ipt.delete_chain(self._make_chain_name(to)) ipt.iptable_restore()
def _kill_lb(self, to): pid_file_path = self._make_pid_file_path(to.lbUuid, to.listenerUuid) pid = linux.find_process_by_cmdline([pid_file_path]) if pid: shell.call('kill %s' % pid) linux.rm_file_force(pid_file_path) linux.rm_file_force( self._make_conf_file_path(to.lbUuid, to.listenerUuid)) ipt = iptables.from_iptables_save() ipt.delete_chain(self._make_chain_name(to)) ipt.iptable_restore()
def heartbeat_on_aliyunnas(): failure = 0 while self.run_fencer(cmd.uuid, created_time): try: time.sleep(cmd.interval) mount_path = cmd.mountPath test_file = os.path.join(mount_path, cmd.heartbeat, '%s-ping-test-file-%s' % (cmd.uuid, kvmagent.HOST_UUID)) touch = shell.ShellCmd('timeout 5 touch %s' % test_file) touch(False) if touch.return_code != 0: logger.debug('touch file failed, cause: %s' % touch.stderr) failure += 1 else: failure = 0 linux.rm_file_force(test_file) continue if failure < cmd.maxAttempts: continue try: logger.warn("aliyun nas storage %s fencer fired!" % cmd.uuid) vm_uuids = kill_vm(cmd.maxAttempts).keys() if vm_uuids: self.report_self_fencer_triggered([cmd.uuid], ','.join(vm_uuids)) # reset the failure count failure = 0 except Exception as e: logger.warn("kill vm failed, %s" % e.message) content = traceback.format_exc() logger.warn("traceback: %s" % content) finally: self.report_storage_status([cmd.uuid], 'Disconnected') except Exception as e: logger.debug('self-fencer on aliyun nas primary storage %s stopped abnormally' % cmd.uuid) content = traceback.format_exc() logger.warn(content) logger.debug('stop self-fencer on aliyun nas primary storage %s' % cmd.uuid)
def ping(self, req): cmd = jsonobject.loads(req[http.REQUEST_BODY]) mount_path = self.mount_path[cmd.uuid] # if nfs service stop, os.path.isdir will hung if not linux.timeout_isdir(mount_path) or not linux.is_mounted(path=mount_path): raise Exception('the mount path[%s] of the nfs primary storage[uuid:%s] is not existing' % (mount_path, cmd.uuid)) test_file = os.path.join(mount_path, '%s-ping-test-file' % uuidhelper.uuid()) touch = shell.ShellCmd('timeout 60 touch %s' % test_file) touch(False) if touch.return_code == 124: raise Exception('unable to access the mount path[%s] of the nfs primary storage[uuid:%s] in 60s, timeout' % (mount_path, cmd.uuid)) elif touch.return_code != 0: touch.raise_error() linux.rm_file_force(test_file) return jsonobject.dumps(NfsResponse())
def clean_iscsi_cache_configuration(path, iscsiServerIp, iscsiServerPort): # clean cache configuration file:/var/lib/iscsi/nodes/iqnxxx/ip,port results = bash.bash_o( ("ls %s/*/ | grep %s | grep %s" % (path, iscsiServerIp, iscsiServerPort))).strip().splitlines() if results is None or len(results) == 0: return for result in results: dpaths = bash.bash_o("dirname %s/*/%s" % (path, result)).strip().splitlines() if dpaths is None or len(dpaths) == 0: continue for dpath in dpaths: ipath = "%s/%s" % (dpath, result) if os.path.isdir(ipath): linux.rm_dir_force(ipath) else: linux.rm_file_force(ipath)
def listvm(self, req): cmd = jsonobject.loads(req[http.REQUEST_BODY]) rsp = ListVmRsp() if cmd.sshPassword and not cmd.sshPrivKey: target, port = getSshTargetAndPort(cmd.libvirtURI) ssh_pswd_file = linux.write_to_temp_file(cmd.sshPassword) if not os.path.exists(V2V_PRIV_KEY) or not os.path.exists(V2V_PUB_KEY): shell.check_run("yes | ssh-keygen -t rsa -N '' -f {}".format(V2V_PRIV_KEY)) cmdstr = "HOME={4} timeout 30 sshpass -f {0} ssh-copy-id -i {5} -p {1} {2} {3}".format( ssh_pswd_file, port, DEF_SSH_OPTS, target, os.path.expanduser("~"), V2V_PUB_KEY) shell.check_run(cmdstr) linux.rm_file_force(ssh_pswd_file) rsp.qemuVersion, rsp.libvirtVersion, rsp.vms, rsp.v2vCaps = listVirtualMachines(cmd.libvirtURI, cmd.saslUser, cmd.saslPass, cmd.sshPrivKey) return jsonobject.dumps(rsp)
def _migrate_volume_segment(self, parent_uuid, resource_uuid, src_install_path, dst_install_path, dst_mon_addr, dst_mon_user, dst_mon_passwd, dst_mon_port, cmd): src_install_path = self._normalize_install_path(src_install_path) dst_install_path = self._normalize_install_path(dst_install_path) traceable_bash = traceable_shell.get_shell(cmd) ssh_cmd, tmp_file = linux.build_sshpass_cmd( dst_mon_addr, dst_mon_passwd, "tee >(md5sum >/tmp/%s_dst_md5) | rbd import-diff - %s" % (resource_uuid, dst_install_path), dst_mon_user, dst_mon_port) r, _, e = traceable_bash.bash_roe( 'set -o pipefail; rbd export-diff {FROM_SNAP} {SRC_INSTALL_PATH} - | tee >(md5sum >/tmp/{RESOURCE_UUID}_src_md5) | {SSH_CMD}' .format(RESOURCE_UUID=resource_uuid, SSH_CMD=ssh_cmd, SRC_INSTALL_PATH=src_install_path, FROM_SNAP='--from-snap ' + parent_uuid if parent_uuid != '' else '')) linux.rm_file_force(tmp_file) if r != 0: logger.error('failed to migrate volume %s: %s' % (src_install_path, e)) return r # compare md5sum of src/dst segments src_segment_md5 = self._read_file_content('/tmp/%s_src_md5' % resource_uuid) dst_segment_md5 = linux.sshpass_call( dst_mon_addr, dst_mon_passwd, 'cat /tmp/%s_dst_md5' % resource_uuid, dst_mon_user, dst_mon_port) if src_segment_md5 != dst_segment_md5: logger.error('check sum mismatch after migration: %s' % src_install_path) return -1 return 0
def create_template_from_root_volume(self, req): cmd = jsonobject.loads(req[http.REQUEST_BODY]) rsp = CreateTemplateFromRootVolumeRsp() try: dirname = os.path.dirname(cmd.installPath) if not os.path.exists(dirname): os.makedirs(dirname, 0755) t_shell = traceable_shell.get_shell(cmd) linux.create_template(cmd.rootVolumePath, cmd.installPath, shell=t_shell) except linux.LinuxError as e: linux.rm_file_force(cmd.installPath) logger.warn(linux.get_exception_stacktrace()) rsp.error = 'unable to create image to root@%s:%s from root volume[%s], %s' % ( cmd.sftpBackupStorageHostName, cmd.installPath, cmd.rootVolumePath, str(e)) rsp.success = False self._set_capacity_to_response(cmd.uuid, rsp) logger.debug('successfully created template[%s] from root volume[%s]' % (cmd.installPath, cmd.rootVolumePath)) return jsonobject.dumps(rsp)
def delete_bootstrap_iso(self, req): cmd = jsonobject.loads(req[http.REQUEST_BODY]) linux.rm_file_force(cmd.isoPath) return jsonobject.dumps(DeleteVirtualRouterBootstrapIsoRsp())
def copy_bits_to_remote(self, req): cmd = jsonobject.loads(req[http.REQUEST_BODY]) if cmd.dstUsername != 'root': raise Exception("cannot support migrate to non-root user host") chain = sum([linux.qcow2_get_file_chain(p) for p in cmd.paths], []) if cmd.sendCommandUrl: Report.url = cmd.sendCommandUrl report = Report(cmd.threadContext, cmd.threadContextStack) report.processType = "LocalStorageMigrateVolume" report.resourceUuid = cmd.volumeUuid PFILE = shell.call('mktemp /tmp/tmp-XXXXXX').strip() PASSWORD_FILE = linux.write_to_temp_file(cmd.dstPassword) start = 10 end = 90 if cmd.stage: start, end = get_scale(cmd.stage) total = 0 for path in set(chain): total = total + os.path.getsize(path) written = 0 def _get_progress(synced): logger.debug( "getProgress in localstorage-agent, synced: %s, total: %s" % (synced, total)) if not os.path.exists(PFILE): return synced fpread = open(PFILE, 'r') lines = fpread.readlines() if not lines: fpread.close() return synced last = str(lines[-1]).strip().split('\r')[-1] if not last or len(last.split()) < 1: fpread.close() return synced line = last.split()[0] if not line.isdigit(): return synced if total > 0: synced = long(line) if synced < total: percent = int( round( float(written + synced) / float(total) * (end - start) + start)) report.progress_report(percent, "report") synced = written fpread.close() return synced for path in set(chain): PATH = path USER = cmd.dstUsername IP = cmd.dstIp PORT = (cmd.dstPort and cmd.dstPort or "22") DIR = os.path.dirname(path) _, _, err = bash_progress_1( # Fixes ZSTAC-13430: handle extremely complex password like ~ ` !@#$%^&*()_+-=[]{}|?<>;:'"/ . 'rsync -av --progress --relative {{PATH}} --rsh="/usr/bin/sshpass -f{{PASSWORD_FILE}} ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p {{PORT}} -l {{USER}}" {{IP}}:/ 1>{{PFILE}}', _get_progress, False) if err: linux.rm_file_force(PASSWORD_FILE) linux.rm_file_force(PFILE) raise Exception('fail to migrate vm to host, because %s' % str(err)) written += os.path.getsize(path) bash_errorout( '/usr/bin/sshpass -f{{PASSWORD_FILE}} ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p {{PORT}} {{USER}}@{{IP}} "/bin/sync {{PATH}}"' ) percent = int( round(float(written) / float(total) * (end - start) + start)) report.progress_report(percent, "report") linux.rm_file_force(PASSWORD_FILE) linux.rm_file_force(PFILE) rsp = AgentResponse() rsp.totalCapacity, rsp.availableCapacity = self._get_disk_capacity( cmd.storagePath) return jsonobject.dumps(rsp)
def download(self, req): rsp = DownloadRsp() def _get_origin_format(path): qcow2_length = 0x9007 if path.startswith('http://') or path.startswith( 'https://') or path.startswith('ftp://'): resp = urllib2.urlopen(path) qhdr = resp.read(qcow2_length) resp.close() elif path.startswith('sftp://'): fd, tmp_file = tempfile.mkstemp() get_header_from_pipe_cmd = "timeout 60 head --bytes=%d %s > %s" % ( qcow2_length, pipe_path, tmp_file) clean_cmd = "pkill -f %s" % pipe_path shell.run( '%s & %s && %s' % (scp_to_pipe_cmd, get_header_from_pipe_cmd, clean_cmd)) qhdr = os.read(fd, qcow2_length) if os.path.exists(tmp_file): os.remove(tmp_file) else: resp = open(path) qhdr = resp.read(qcow2_length) resp.close() if len(qhdr) < qcow2_length: return "raw" return get_image_format_from_buf(qhdr) def get_origin_format(fpath, fail_if_has_backing_file=True): image_format = _get_origin_format(fpath) if image_format == "derivedQcow2" and fail_if_has_backing_file: raise Exception('image has backing file or %s is not exist!' % fpath) return image_format cmd = jsonobject.loads(req[http.REQUEST_BODY]) shell = traceable_shell.get_shell(cmd) pool, image_name = self._parse_install_path(cmd.installPath) tmp_image_name = 'tmp-%s' % image_name @rollbackable def _1(): shell.check_run('rbd rm %s/%s' % (pool, tmp_image_name)) def _getRealSize(length): '''length looks like: 10245K''' logger.debug(length) if not length[-1].isalpha(): return length units = { "g": lambda x: x * 1024 * 1024 * 1024, "m": lambda x: x * 1024 * 1024, "k": lambda x: x * 1024, } try: if not length[-1].isalpha(): return length return units[length[-1].lower()](int(length[:-1])) except: logger.warn(linux.get_exception_stacktrace()) return length # whether we have an upload request if cmd.url.startswith(self.UPLOAD_PROTO): self._prepare_upload(cmd) rsp.size = 0 rsp.uploadPath = self._get_upload_path(req) self._set_capacity_to_response(rsp) return jsonobject.dumps(rsp) if cmd.sendCommandUrl: Report.url = cmd.sendCommandUrl report = Report(cmd.threadContext, cmd.threadContextStack) report.processType = "AddImage" report.resourceUuid = cmd.imageUuid report.progress_report("0", "start") url = urlparse.urlparse(cmd.url) if url.scheme in ('http', 'https', 'ftp'): image_format = get_origin_format(cmd.url, True) cmd.url = linux.shellquote(cmd.url) # roll back tmp ceph file after import it _1() _, PFILE = tempfile.mkstemp() content_length = shell.call( """curl -sLI %s|awk '/[cC]ontent-[lL]ength/{print $NF}'""" % cmd.url).splitlines()[-1] total = _getRealSize(content_length) def _getProgress(synced): last = linux.tail_1(PFILE).strip() if not last or len(last.split( )) < 1 or 'HTTP request sent, awaiting response' in last: return synced logger.debug("last synced: %s" % last) written = _getRealSize(last.split()[0]) if total > 0 and synced < written: synced = written if synced < total: percent = int(round(float(synced) / float(total) * 90)) report.progress_report(percent, "report") return synced logger.debug("content-length is: %s" % total) _, _, err = shell.bash_progress_1( 'set -o pipefail;wget --no-check-certificate -O - %s 2>%s| rbd import --image-format 2 - %s/%s' % (cmd.url, PFILE, pool, tmp_image_name), _getProgress) if err: raise err actual_size = linux.get_file_size_by_http_head(cmd.url) if os.path.exists(PFILE): os.remove(PFILE) elif url.scheme == 'sftp': port = (url.port, 22)[url.port is None] _, PFILE = tempfile.mkstemp() ssh_pswd_file = None pipe_path = PFILE + "fifo" scp_to_pipe_cmd = "scp -P %d -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null %s@%s:%s %s" % ( port, url.username, url.hostname, url.path, pipe_path) sftp_command = "sftp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=no -P %s -b /dev/stdin %s@%s" % ( port, url.username, url.hostname) + " <<EOF\n%s\nEOF\n" if url.password is not None: ssh_pswd_file = linux.write_to_temp_file(url.password) scp_to_pipe_cmd = 'sshpass -f %s %s' % (ssh_pswd_file, scp_to_pipe_cmd) sftp_command = 'sshpass -f %s %s' % (ssh_pswd_file, sftp_command) actual_size = shell.call( sftp_command % ("ls -l " + url.path)).splitlines()[1].strip().split()[4] os.mkfifo(pipe_path) image_format = get_origin_format(cmd.url, True) cmd.url = linux.shellquote(cmd.url) # roll back tmp ceph file after import it _1() def _get_progress(synced): if not os.path.exists(PFILE): return synced last = linux.tail_1(PFILE).strip() if not last or not last.isdigit(): return synced report.progress_report(int(last) * 90 / 100, "report") return synced get_content_from_pipe_cmd = "pv -s %s -n %s 2>%s" % ( actual_size, pipe_path, PFILE) import_from_pipe_cmd = "rbd import --image-format 2 - %s/%s" % ( pool, tmp_image_name) _, _, err = shell.bash_progress_1( 'set -o pipefail; %s & %s | %s' % (scp_to_pipe_cmd, get_content_from_pipe_cmd, import_from_pipe_cmd), _get_progress) if ssh_pswd_file: linux.rm_file_force(ssh_pswd_file) linux.rm_file_force(PFILE) linux.rm_file_force(pipe_path) if err: raise err elif url.scheme == 'file': src_path = cmd.url.lstrip('file:') src_path = os.path.normpath(src_path) if not os.path.isfile(src_path): raise Exception('cannot find the file[%s]' % src_path) image_format = get_origin_format(src_path, True) # roll back tmp ceph file after import it _1() shell.check_run("rbd import --image-format 2 %s %s/%s" % (src_path, pool, tmp_image_name)) actual_size = os.path.getsize(src_path) else: raise Exception('unknown url[%s]' % cmd.url) file_format = shell.call( "set -o pipefail; %s rbd:%s/%s | grep 'file format' | cut -d ':' -f 2" % (qemu_img.subcmd('info'), pool, tmp_image_name)) file_format = file_format.strip() if file_format not in ['qcow2', 'raw']: raise Exception('unknown image format: %s' % file_format) if file_format == 'qcow2': conf_path = None try: with open('/etc/ceph/ceph.conf', 'r') as fd: conf = fd.read() conf = '%s\n%s\n' % (conf, 'rbd default format = 2') conf_path = linux.write_to_temp_file(conf) shell.check_run( '%s -f qcow2 -O rbd rbd:%s/%s rbd:%s/%s:conf=%s' % (qemu_img.subcmd('convert'), pool, tmp_image_name, pool, image_name, conf_path)) shell.check_run('rbd rm %s/%s' % (pool, tmp_image_name)) finally: if conf_path: os.remove(conf_path) else: shell.check_run('rbd mv %s/%s %s/%s' % (pool, tmp_image_name, pool, image_name)) report.progress_report("100", "finish") @rollbackable def _2(): shell.check_run('rbd rm %s/%s' % (pool, image_name)) _2() o = shell.call('rbd --format json info %s/%s' % (pool, image_name)) image_stats = jsonobject.loads(o) rsp.size = long(image_stats.size_) rsp.actualSize = actual_size if image_format == "qcow2": rsp.format = "raw" else: rsp.format = image_format self._set_capacity_to_response(rsp) return jsonobject.dumps(rsp)
def migrate_bits(self, req): cmd = jsonobject.loads(req[http.REQUEST_BODY]) rsp = NfsToNfsMigrateBitsRsp() mount_path = cmd.mountPath dst_folder_path = cmd.dstFolderPath temp_dir = None fd, PFILE = tempfile.mkstemp() os.close(fd) f = open(PFILE, 'r') try: if not cmd.isMounted: linux.is_valid_nfs_url(cmd.url) temp_dir = tempfile.mkdtemp() # dst folder is absolute path mount_path = temp_dir + mount_path dst_folder_path = temp_dir + dst_folder_path if not linux.is_mounted(mount_path, cmd.url): linux.mount(cmd.url, mount_path, cmd.options, "nfs4") # begin migration, then check md5 sums linux.mkdir(dst_folder_path) t_shell = traceable_shell.get_shell(cmd) rsync_excludes = "" md5_excludes = "" if cmd.filtPaths: for filtPath in cmd.filtPaths: # filtPath cannot start with '/', because it must be a relative path if filtPath.startswith('/'): filtPath = filtPath[1:] if filtPath != '': rsync_excludes = rsync_excludes + " --exclude=%s" % filtPath md5_excludes = md5_excludes + " ! -path %s/%s" % ( cmd.srcFolderPath, filtPath) total_size = int( shell.call( "rsync -aznv %s/ %s %s | grep -o -P 'total size is \K\d*'" % (cmd.srcFolderPath, dst_folder_path, rsync_excludes))) stage = get_task_stage(cmd) reporter = Report.from_spec(cmd, "MigrateVolume") def _get_progress(synced): def get_written(regex): matcher = re.match(regex, line) return int(matcher.group(1)) if matcher else 0 lines = f.readlines() writing = 0 for line in lines: if line[1] == ' ' and line[-1] == '\n': synced += get_written(r'\s.*?(\d+)\s+100%') elif line[-1] == '\r' and line[1] == ' ': writing = get_written(r'.*?(\d+)\s+\d+%[^\r]*\r$') reporter.progress_report( get_exact_percent( float(synced + writing) / total_size * 100, stage)) return synced t_shell.bash_progress_1( "rsync -az --progress %s/ %s %s > %s" % (cmd.srcFolderPath, dst_folder_path, rsync_excludes, PFILE), _get_progress) src_md5 = t_shell.call( "find %s -type f %s -exec md5sum {} \; | awk '{ print $1 }' | sort | md5sum" % (cmd.srcFolderPath, md5_excludes)) dst_md5 = t_shell.call( "find %s -type f -exec md5sum {} \; | awk '{ print $1 }' | sort | md5sum" % dst_folder_path) if src_md5 != dst_md5: rsp.error = "failed to copy files from %s to %s, md5sum not match" % ( cmd.srcFolderPath, dst_folder_path) rsp.success = False if not cmd.isMounted: linux.umount(mount_path) finally: if temp_dir is not None: return_code = shell.run("mount | grep '%s'" % temp_dir) if return_code != 0: # in case dir is not empty try: os.rmdir(temp_dir) except OSError as e: logger.warn("delete temp_dir %s failed: %s", (temp_dir, str(e))) else: logger.warn( "temp_dir %s still had mounted destination primary storage, skip cleanup operation" % temp_dir) f.close() linux.rm_file_force(PFILE) return jsonobject.dumps(rsp)
def get_metadata_file(self, bs_uuid, file_name): local_file_name = "/tmp/%s" % file_name linux.rm_file_force(local_file_name) bash_ro("rados -p bak-t-%s get %s %s" % (bs_uuid, file_name, local_file_name))
def _0(): linux.rm_file_force(cmd.installPath)
def _0(): linux.rm_file_force(pid_file)
def delpid(self): linux.rm_file_force(self.pidfile)
def _0(): linux.rm_file_force(pid_file)
def delete_bootstrap_iso(self, req): cmd = jsonobject.loads(req[http.REQUEST_BODY]) linux.rm_file_force(cmd.isoPath) return jsonobject.dumps(DeleteVirtualRouterBootstrapIsoRsp())
def copy_bits_to_remote(self, req): cmd = jsonobject.loads(req[http.REQUEST_BODY]) if cmd.dstUsername != 'root': raise Exception("cannot support migrate to non-root user host") chain = sum([linux.qcow2_get_file_chain(p) for p in cmd.paths], []) if cmd.sendCommandUrl: Report.url = cmd.sendCommandUrl report = Report(cmd.threadContext, cmd.threadContextStack) report.processType = "LocalStorageMigrateVolume" report.resourceUuid = cmd.volumeUuid PFILE = shell.call('mktemp /tmp/tmp-XXXXXX').strip() PASSWORD_FILE = linux.write_to_temp_file(cmd.dstPassword) start = 10 end = 90 if cmd.stage: start, end = get_scale(cmd.stage) total = 0 for path in set(chain): total = total + os.path.getsize(path) written = 0 def _get_progress(synced): logger.debug("getProgress in localstorage-agent, synced: %s, total: %s" % (synced, total)) if not os.path.exists(PFILE): return synced fpread = open(PFILE, 'r') lines = fpread.readlines() if not lines: fpread.close() return synced last = str(lines[-1]).strip().split('\r')[-1] if not last or len(last.split()) < 1: fpread.close() return synced line = last.split()[0] if not line.isdigit(): return synced if total > 0: synced = long(line) if synced < total: percent = int(round(float(written + synced) / float(total) * (end - start) + start)) report.progress_report(percent, "report") synced = written fpread.close() return synced for path in set(chain): PATH = path USER = cmd.dstUsername IP = cmd.dstIp PORT = (cmd.dstPort and cmd.dstPort or "22") DIR = os.path.dirname(path) _, _, err = bash_progress_1( # Fixes ZSTAC-13430: handle extremely complex password like ~ ` !@#$%^&*()_+-=[]{}|?<>;:'"/ . 'rsync -av --progress --relative {{PATH}} --rsh="/usr/bin/sshpass -f{{PASSWORD_FILE}} ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p {{PORT}} -l {{USER}}" {{IP}}:/ 1>{{PFILE}}', _get_progress, False) if err: linux.rm_file_force(PASSWORD_FILE) linux.rm_file_force(PFILE) raise Exception('fail to migrate vm to host, because %s' % str(err)) written += os.path.getsize(path) bash_errorout('/usr/bin/sshpass -f{{PASSWORD_FILE}} ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p {{PORT}} {{USER}}@{{IP}} "/bin/sync {{PATH}}"') percent = int(round(float(written) / float(total) * (end - start) + start)) report.progress_report(percent, "report") linux.rm_file_force(PASSWORD_FILE) linux.rm_file_force(PFILE) rsp = AgentResponse() rsp.totalCapacity, rsp.availableCapacity = self._get_disk_capacity(cmd.storagePath) return jsonobject.dumps(rsp)
def get_metadata_file(self, bs_uuid, file_name): local_file_name = "/tmp/%s" % file_name linux.rm_file_force(local_file_name) bash_ro("rados -p bak-t-%s get %s %s" % (bs_uuid, file_name, local_file_name))
def put_metadata_file(self, bs_uuid, file_name): local_file_name = "/tmp/%s" % file_name ret, output = bash_ro("rados -p bak-t-%s put %s %s" % (bs_uuid, file_name, local_file_name)) if ret == 0: linux.rm_file_force(local_file_name)
def put_metadata_file(self, bs_uuid, file_name): local_file_name = "/tmp/%s" % file_name ret, output = bash_ro("rados -p bak-t-%s put %s %s" % (bs_uuid, file_name, local_file_name)) if ret == 0: linux.rm_file_force(local_file_name)