Esempio n. 1
0
    def ping(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = PingRsp()

        facts = bash_o('ceph -s -f json')
        mon_facts = jsonobject.loads(facts)
        found = False
        for mon in mon_facts.monmap.mons:
            if cmd.monAddr in mon.addr:
                found = True
                break

        if not found:
            rsp.success = False
            rsp.failure = "MonAddrChanged"
            rsp.error = 'The mon addr is changed on the mon server[uuid:%s], not %s anymore.' \
                        'Reconnect the ceph primary storage' \
                        ' may solve this issue' % (cmd.monUuid, cmd.monAddr)
            return jsonobject.dumps(rsp)

        pool, objname = cmd.testImagePath.split('/')

        create_img = shell.ShellCmd("echo zstack | rados -p '%s' put '%s' -" % (pool, objname))
        create_img(False)
        if create_img.return_code != 0:
            rsp.success = False
            rsp.failure = 'UnableToCreateFile'
            rsp.error = "%s %s" % (create_img.stderr, create_img.stdout)
        else:
            shell.run("rados -p '%s' rm '%s'" % (pool, objname))

        return jsonobject.dumps(rsp)
    def get_images_metadata(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        valid_images_info = ""
        bs_sftp_info_file = cmd.backupStoragePath + '/' + self.SFTP_METADATA_FILE
        image_uuid_list = []
        with open(bs_sftp_info_file) as fd:
            images_info = fd.read()
            for image_info in images_info.split('\n'):
                if image_info != '':
                    image_json = jsonobject.loads(image_info)
                    # todo support multiple bs
                    image_uuid = image_json['uuid']
                    image_install_path = image_json["backupStorageRefs"][0]["installPath"]
                    if image_uuid in image_uuid_list:
                        logger.debug("duplicate uuid %s, ignore" % image_json["uuid"])
                        continue
                    image_uuid_list.append(image_uuid)
                    ret = bash_r("ls %s" % image_install_path)
                    if ret == 0 :
                        logger.info("Check image %s install path %s successfully!" % (image_uuid, image_install_path))
                        valid_images_info = image_info + '\n' + valid_images_info
                    else:
                        logger.warn("Image %s install path %s is invalid!" % (image_uuid, image_install_path))

        rsp = GetImageMetaDataResponse()
        rsp.imagesMetaData = valid_images_info
        return jsonobject.dumps(rsp)
Esempio n. 3
0
    def get_images_metadata(self, req):
        logger.debug("meilei: get images metadata")
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        pool_name = cmd.poolName
        bs_uuid = pool_name.split("-")[-1]
        valid_images_info = ""
        self.get_metadata_file(bs_uuid, self.CEPH_METADATA_FILE)
        last_image_install_path = ""
        bs_ceph_info_file = "/tmp/%s" % self.CEPH_METADATA_FILE
        with open(bs_ceph_info_file) as fd:
            images_info = fd.read()
            for image_info in images_info.split('\n'):
                if image_info != '':
                    image_json = jsonobject.loads(image_info)
                    # todo support multiple bs
                    image_uuid = image_json['uuid']
                    image_install_path = image_json["backupStorageRefs"][0]["installPath"]
                    ret = bash_r("rbd info %s" % image_install_path.split("//")[1])
                    if ret == 0 :
                        logger.info("Check image %s install path %s successfully!" % (image_uuid, image_install_path))
                        if image_install_path != last_image_install_path:
                            valid_images_info = image_info + '\n' + valid_images_info
                            last_image_install_path = image_install_path
                    else:
                        logger.warn("Image %s install path %s is invalid!" % (image_uuid, image_install_path))

        self.put_metadata_file(bs_uuid, self.CEPH_METADATA_FILE)
        rsp = GetImageMetaDataResponse()
        rsp.imagesMetadata= valid_images_info
        return jsonobject.dumps(rsp)
Esempio n. 4
0
    def async_call_wait_for_complete(self, apicmd, exception_on_error=True, interval=500, fail_soon=False):
        self._check_not_none_field(apicmd)
        timeout = apicmd.timeout
        if not timeout:
            timeout = 1800000
        cmd = {apicmd.FULL_NAME: apicmd}
        logger.debug("async call[url: %s, request: %s]" % (self.api_url, jsonobject.dumps(cmd)))
        jstr = http.json_dump_post(self.api_url, cmd, fail_soon=fail_soon)
        rsp = jsonobject.loads(jstr)
        if rsp.state == 'Done':
            logger.debug("async call[url: %s, response: %s]" % (self.api_url, rsp.result))
            reply = jsonobject.loads(rsp.result)
            (name, event) = (reply.__dict__.items()[0])
            if exception_on_error and not event.success:
                raise ApiError('API call[%s] failed because %s' % (name, self._error_code_to_string(event.error)))
            return name, event

        curr = 0
        finterval = float(float(interval) / float(1000))
        ret_uuid = rsp.uuid
        while rsp.state != 'Done' and curr < timeout:
            time.sleep(finterval)
            rsp = self._get_response(ret_uuid)
            curr += interval

        if curr >= timeout:
            raise ApiError('API call[%s] timeout after %dms' % (apicmd.FULL_NAME, curr))

        logger.debug("async call[url: %s, response: %s] after %dms" % (self.api_url, rsp.result, curr))
        reply = jsonobject.loads(rsp.result)
        (name, event) = (reply.__dict__.items()[0])
        if exception_on_error and not event.success:
            raise ApiError('API call[%s] failed because %s' % (name, self._error_code_to_string(event.error)))
        return name, event
Esempio n. 5
0
    def create_snapshot(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        spath = self._normalize_install_path(cmd.snapshotPath)

        do_create = True
        if cmd.skipOnExisting:
            image_name, sp_name = spath.split('@')
            o = shell.call('rbd --format json snap ls %s' % image_name)
            o = jsonobject.loads(o)
            for s in o:
                if s.name_ == sp_name:
                    do_create = False

        if do_create:
            o = shell.ShellCmd('rbd snap create %s' % spath)
            o(False)
            if o.return_code != 0:
                shell.run("rbd snap rm %s" % spath)
                o.raise_error()


        rsp = CreateSnapshotRsp()
        rsp.size = self._get_file_size(spath)
        self._set_capacity_to_response(rsp)
        return jsonobject.dumps(rsp)
Esempio n. 6
0
    def init(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])

        o = shell.call('ceph mon_status')
        mon_status = jsonobject.loads(o)
        fsid = mon_status.monmap.fsid_

        existing_pools = shell.call('ceph osd lspools')
        for pool in cmd.pools:
            if pool.predefined and pool.name not in existing_pools:
                raise Exception('cannot find pool[%s] in the ceph cluster, you must create it manually' % pool.name)
            elif pool.name not in existing_pools:
                shell.call('ceph osd pool create %s 128' % pool.name)

        rsp = InitRsp()

        if cmd.nocephx is False:
            o = shell.call("ceph -f json auth get-or-create client.zstack mon 'allow r' osd 'allow *' 2>/dev/null").strip(
                ' \n\r\t')
            o = jsonobject.loads(o)
            rsp.userKey = o[0].key_

        rsp.fsid = fsid
        self._set_capacity_to_response(rsp)

        return jsonobject.dumps(rsp)
Esempio n. 7
0
    def ping(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = PingRsp()

        facts = bash_o('ceph -s -f json')
        mon_facts = jsonobject.loads(facts)
        found = False
        for mon in mon_facts.monmap.mons:
            if cmd.monAddr in mon.addr:
                found = True
                break

        if not found:
            rsp.success = False
            rsp.failure = "MonAddrChanged"
            rsp.error = 'The mon addr is changed on the mon server[uuid:%s], not %s anymore.' \
                        'Reconnect the ceph primary storage' \
                        ' may solve this issue' % (cmd.monUuid, cmd.monAddr)
            return jsonobject.dumps(rsp)


        create_img = shell.ShellCmd('rbd create %s --image-format 2 --size 1' % cmd.testImagePath)
        create_img(False)
        if create_img.return_code != 0:
            rsp.success = False
            rsp.failure = 'UnableToCreateFile'
            rsp.error = "%s %s" % (create_img.stderr, create_img.stdout)
        else:
            rm_img = shell.ShellCmd('rbd rm %s' % cmd.testImagePath)
            rm_img(False)

        return jsonobject.dumps(rsp)
Esempio n. 8
0
 def get_volume_snapinfos(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     vpath = self._normalize_install_path(cmd.volumePath)
     ret = shell.call('rbd --format=json snap ls %s' % vpath)
     rsp = GetVolumeSnapInfosRsp()
     rsp.snapInfos = jsonobject.loads(ret)
     self._set_capacity_to_response(rsp)
     return jsonobject.dumps(rsp)
Esempio n. 9
0
    def ping(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])

        facts = bash_o('ceph -s -f json')
        mon_facts = jsonobject.loads(facts)
        found = False
        for mon in mon_facts.monmap.mons:
            if cmd.monAddr in mon.addr:
                found = True
                break

        rsp = PingRsp()

        if not found:
            rsp.success = False
            rsp.failure = "MonAddrChanged"
            rsp.error = 'The mon addr is changed on the mon server[uuid:%s], not %s anymore.' \
                        'Reconnect the ceph primary storage' \
                        ' may solve this issue' % (cmd.monUuid, cmd.monAddr)
            return jsonobject.dumps(rsp)

        def retry(times=3, sleep_time=3):
            def wrap(f):
                @functools.wraps(f)
                def inner(*args, **kwargs):
                    for i in range(0, times):
                        try:
                            return f(*args, **kwargs)
                        except Exception as e:
                            logger.error(e)
                            time.sleep(sleep_time)
                    rsp.error = ("Still failed after retry. Below is detail:\n %s" % e)

                return inner

            return wrap

        @retry()
        def doPing():
            # try to delete test file, ignore the result
            pool, objname = cmd.testImagePath.split('/')
            bash_r("rados -p '%s' rm '%s'" % (pool, objname))
            r, o, e = bash_roe("echo zstack | timeout 60 rados -p '%s' put '%s' -" % (pool, objname))
            if r != 0:
                rsp.success = False
                rsp.failure = "UnableToCreateFile"
                if r == 124:
                    # timeout happened
                    rsp.error = 'failed to create heartbeat object on ceph, timeout after 60s, %s %s' % (e, o)
                    raise Exception(rsp.error)
                else:
                    rsp.error = "%s %s" % (e, o)

        doPing()

        self._set_capacity_to_response(rsp)
        return jsonobject.dumps(rsp)
Esempio n. 10
0
    def get_facts(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        o = shell.call('ceph mon_status')
        mon_status = jsonobject.loads(o)
        fsid = mon_status.monmap.fsid_

        rsp = GetFactsRsp()
        rsp.fsid = fsid
        return jsonobject.dumps(rsp)
Esempio n. 11
0
    def download(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])

        pool, image_name = self._parse_install_path(cmd.installPath)
        tmp_image_name = 'tmp-%s' % image_name

        if cmd.url.startswith('http://') or cmd.url.startswith('https://'):
            shell.call('set -o pipefail; wget --no-check-certificate -q -O - %s | rbd import --image-format 2 - %s/%s'
                       % (cmd.url, pool, tmp_image_name))
            actual_size = linux.get_file_size_by_http_head(cmd.url)
        elif cmd.url.startswith('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)
            shell.call("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)

        @rollbackable
        def _1():
            shell.call('rbd rm %s/%s' % (pool, tmp_image_name))
        _1()

        file_format = shell.call("set -o pipefail; qemu-img info rbd:%s/%s | grep 'file format' | cut -d ':' -f 2" % (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.call('qemu-img convert -f qcow2 -O rbd rbd:%s/%s rbd:%s/%s:conf=%s' % (pool, tmp_image_name, pool, image_name, conf_path))
                shell.call('rbd rm %s/%s' % (pool, tmp_image_name))
            finally:
                if conf_path:
                    os.remove(conf_path)
        else:
            shell.call('rbd mv %s/%s %s/%s' % (pool, tmp_image_name, pool, image_name))

        o = shell.call('rbd --format json info %s/%s' % (pool, image_name))
        image_stats = jsonobject.loads(o)

        rsp = DownloadRsp()
        rsp.size = long(image_stats.size_)
        rsp.actualSize = actual_size
        self._set_capacity_to_response(rsp)
        return jsonobject.dumps(rsp)
Esempio n. 12
0
 def sync_call(self, apicmd, exception_on_error=True, fail_soon=False):
     self._check_not_none_field(apicmd)
     cmd = {apicmd.FULL_NAME: apicmd}
     logger.debug("sync_call[url: %s, request: %s]" % (self.api_url, jsonobject.dumps(cmd)))
     jstr = http.json_dump_post(self.api_url, cmd, fail_soon=fail_soon)
     logger.debug("sync_call[url: %s, response: %s]" % (self.api_url, jstr))
     rsp = jsonobject.loads(jstr)
     reply = jsonobject.loads(rsp.result)
     (name, r) = reply.__dict__.items()[0]
     if exception_on_error:
         if not r.success:
             raise ApiError('API call[%s] failed because %s' % (name, self._error_code_to_string(r.error)))
     return name, r
Esempio n. 13
0
    def delete(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        path = self._normalize_install_path(cmd.installPath)

        o = shell.call('rbd snap ls --format json %s' % path)
        o = jsonobject.loads(o)
        if len(o) > 0:
            raise Exception('unable to delete %s; the volume still has snapshots' % cmd.installPath)

        shell.call('rbd rm %s' % path)

        rsp = AgentResponse()
        self._set_capacity_to_response(rsp)
        return jsonobject.dumps(rsp)
Esempio n. 14
0
 def add_dhcp_entry(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     entries = []
     gateways = []
     for e in cmd.dhcpEntries:
         entry = DhcpEntry.from_dhcp_info(e)
         entries.append(entry)
         gateways.append(entry.gateway)
         
     if cmd.rebuild:
         self._rebuild_all(entries)
     else:
         self._merge(entries)
     
     rsp = AddDhcpEntryRsp()
     try:
         if self._add_dhcp_range_if_need(gateways):
             self._restart_dnsmasq()
         else:
             self._refresh_dnsmasq()
     except virtualrouter.VirtualRouterError as e:
         logger.warn(linux.get_exception_stacktrace())
         rsp.error = str(e)
         rsp.success = False
     
     return jsonobject.dumps(rsp)
Esempio n. 15
0
    def _set_capacity_to_response(self, rsp):
        o = shell.call('ceph df -f json')
        df = jsonobject.loads(o)

        if df.stats.total_bytes__ is not None:
            total = long(df.stats.total_bytes_)
        elif df.stats.total_space__ is not None:
            total = long(df.stats.total_space__) * 1024
        else:
            raise Exception('unknown ceph df output: %s' % o)

        if df.stats.total_avail_bytes__ is not None:
            avail = long(df.stats.total_avail_bytes_)
        elif df.stats.total_avail__ is not None:
            avail = long(df.stats.total_avail_) * 1024
        else:
            raise Exception('unknown ceph df output: %s' % o)

        rsp.totalCapacity = total
        rsp.availableCapacity = avail
        rsp.xsky = isXsky()

        if not df.pools:
            return

        pools = ceph.getCephPoolsCapacity()
        if not pools:
            return

        rsp.poolCapacities = []
        for pool in pools:
            poolCapacity = CephPoolCapacity(pool.poolName, pool.availableCapacity, pool.replicatedSize, pool.usedCapacity, pool.poolTotalSize)
            rsp.poolCapacities.append(poolCapacity)
 def downloadfromimagestore(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     self.imagestore_client.download_from_imagestore(cmd.cacheDir, cmd.hostname, cmd.backupStorageInstallPath,
                                                     cmd.primaryStorageInstallPath)
     rsp = AliyunNasResponse()
     rsp.totalCapacity, rsp.availableCapacity = self._get_disk_capacity(cmd.uuid)
     return jsonobject.dumps(rsp)
Esempio n. 17
0
    def remove_dhcp_entry(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = RemoveDhcpEntryRsp()
        try:
            for e in cmd.dhcpEntries:
                net_dev = shell.call("ifconfig|grep -i %s|awk '{print $1}'" % e.vrNicMac)
                net_dev = net_dev.strip('\t\r\n ')
                mac2 = e.mac.replace(':', '')
                shell.call("sed -i '/%s/d' %s; \
                        sed -i '/^$/d' %s; \
                        sed -i '/%s/d' %s; \
                        sed -i '/^$/d' %s; \
                        sed -i '/%s/d' %s; \
                        sed -i '/^$/d' %s; \
                        dhcp_release %s %s %s"\
                        % (e.mac, self.HOST_DHCP_FILE, \
                        self.HOST_DHCP_FILE, \
                        mac2, self.HOST_OPTION_FILE, \
                        self.HOST_OPTION_FILE, \
                        e.ip, self.HOST_DNS_FILE, \
                        self.HOST_DNS_FILE, \
                        net_dev, e.ip, e.mac))

        except virtualrouter.VirtualRouterError as e:
            logger.warn(linux.get_exception_stacktrace())
            rsp.error = str(e)
            rsp.success = False

        return jsonobject.dumps(rsp)
Esempio n. 18
0
    def delete(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        for to in cmd.lbs:
            self._kill_lb(to)

        rsp = virtualrouter.AgentResponse()
        return jsonobject.dumps(rsp)
    def deletebits(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = AliyunNasResponse()
        self.delNasBits(cmd.folder, cmd.path)

        rsp.totalCapacity, rsp.availableCapacity = self._get_disk_capacity(cmd.uuid)
        return jsonobject.dumps(rsp)
Esempio n. 20
0
    def remove_eip(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = RemoveEipRsp()

        self._remove_eip(cmd.eip)

        return jsonobject.dumps(rsp)
Esempio n. 21
0
    def sync_eip(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = SyncEipRsp()

        def remove_eip_chain(table):
            for c in table.children:
                if c.name.startswith('eip-'):
                    c.delete()

        ipt = iptables.from_iptables_save()
        nat = ipt.get_table(ipt.NAT_TABLE_NAME)
        if nat:
            remove_eip_chain(nat)
        filter_table = ipt.get_table(ipt.FILTER_TABLE_NAME)
        if filter_table:
            remove_eip_chain(filter_table)
        ipt.iptable_restore()

        try:
            for eip in cmd.eips:
                self._create_eip(eip)
        except virtualrouter.VirtualRouterError as e:
            logger.warning(linux.get_exception_stacktrace())
            rsp.error = str(e)
            rsp.success = False

        return jsonobject.dumps(rsp)
Esempio n. 22
0
    def unprotect_snapshot(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        spath = self._normalize_install_path(cmd.snapshotPath)

        shell.call('rbd snap unprotect %s' % spath)

        return jsonobject.dumps(AgentResponse())
Esempio n. 23
0
    def download_from_sftp(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = DownloadBitsFromSftpBackupStorageRsp()
        sub_vol_dir = os.path.dirname(cmd.primaryStorageInstallPath)
        if not os.path.exists(sub_vol_dir):
            parent_dir = os.path.dirname(sub_vol_dir)
            shell.call('mkdir -p %s' % parent_dir)
            shell.call('btrfs subvolume create %s' % sub_vol_dir)

        linux.scp_download(cmd.hostname, cmd.sshKey, cmd.backupStorageInstallPath, cmd.primaryStorageInstallPath)

        def get_image_format():
            out = shell.call('qemu-img info %s' % cmd.primaryStorageInstallPath)
            for l in out.split('\n'):
                if 'file format' in l:
                    _, f = l.split(':')
                    return f.strip()
            raise Exception('cannot get image format of %s, qemu-img info outputs:\n%s\n' % (cmd.primaryStorageInstallPath, out))

        f = get_image_format()
        if 'qcow2' in f:
            shell.call('/usr/bin/qemu-img convert -f qcow2 -O raw %s %s.img' % (cmd.primaryStorageInstallPath, cmd.primaryStorageInstallPath))
            shell.call('mv %s.img %s' % (cmd.primaryStorageInstallPath, cmd.primaryStorageInstallPath))
        elif 'raw' in f:
            pass
        else:
            raise Exception('unsupported image format[%s] of %s' % (f, cmd.primaryStorageInstallPath))

        rsp.totalCapacity, rsp.availableCapacity = self._get_disk_capacity()
        logger.debug('downloaded %s:%s to %s' % (cmd.hostname, cmd.backupStorageInstallPath, cmd.primaryStorageInstallPath))
        return jsonobject.dumps(rsp)
Esempio n. 24
0
    def create(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])

        path = self._normalize_install_path(cmd.installPath)
        rsp = CreateEmptyVolumeRsp()

        call_string = None
        if isXsky():
            # do NOT round to MB
            call_string = 'rbd create --size %dB --image-format 2 %s' % (cmd.size, path)
            rsp.size = cmd.size
        else:
            size_M = sizeunit.Byte.toMegaByte(cmd.size) + 1
            call_string = 'rbd create --size %s --image-format 2 %s' % (size_M, path)
            rsp.size = cmd.size + sizeunit.MegaByte.toByte(1)

        if cmd.shareable:
            call_string = call_string + " --image-shared"

        skip_cmd = "rbd info %s ||" % path if cmd.skipIfExisting else ""
        shell.call(skip_cmd + call_string)


        self._set_capacity_to_response(rsp)
        return jsonobject.dumps(rsp)
Esempio n. 25
0
 def purge_snapshots(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     vpath = self._normalize_install_path(cmd.volumePath)
     shell.call('rbd snap purge %s' % vpath)
     rsp = AgentResponse()
     self._set_capacity_to_response(rsp)
     return jsonobject.dumps(rsp)
Esempio n. 26
0
    def cp(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        src_path = self._normalize_install_path(cmd.srcPath)
        dst_path = self._normalize_install_path(cmd.dstPath)

        if cmd.sendCommandUrl:
            Report.url = cmd.sendCommandUrl

        report = Report(cmd.threadContext, cmd.threadContextStack)
        report.processType = "CephCpVolume"
        _, PFILE = tempfile.mkstemp()
        stage = (cmd.threadContext['task-stage'], "10-90")[cmd.threadContext['task-stage'] is None]

        def _get_progress(synced):
            if not Report.url:
                return synced

            logger.debug("getProgress in ceph-agent")
            percent = shell.call("tail -1 %s | grep -o '1\?[0-9]\{1,2\}%%' | tail -1" % PFILE).strip(' \t\n\r%')
            if percent and Report.url:
                report.progress_report(get_exact_percent(percent, stage), "report")
            return synced

        _, _, err = bash_progress_1('rbd cp %s %s 2> %s' % (src_path, dst_path, PFILE), _get_progress)

        if os.path.exists(PFILE):
            os.remove(PFILE)

        if err:
            raise err

        rsp = CpRsp()
        rsp.size = self._get_file_size(dst_path)
        self._set_capacity_to_response(rsp)
        return jsonobject.dumps(rsp)
Esempio n. 27
0
    def migrate_volume_segment(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = AgentResponse()
        src_install_path = self._normalize_install_path(cmd.srcInstallPath)
        dst_install_path = self._normalize_install_path(cmd.dstInstallPath)
        src_size = self._get_file_size(src_install_path)
        dst_size = self._get_dst_volume_size(dst_install_path, cmd.dstMonHostname, cmd.dstMonSshUsername, cmd.dstMonSshPassword, cmd.dstMonSshPort)
        if dst_size > src_size:
            if cmd.isXsky:
                # xsky / ceph -> xsky, size must be equal
                rsp.success = False
                rsp.error = "Failed to migrate volume segment because dst size: %s > src size: %s" % (dst_size, src_size)
                return jsonobject.dumps(rsp)
            elif isXsky() == False:
                # ceph -> ceph, don't check size
                rsp.success = True
            else:
                # xsky -> ceph, not supported
                rsp.success = False
                rsp.error = "Failed to migrate volume segment because xsky migrate to ceph is not supported now"
                return jsonobject.dumps(rsp)
        if dst_size < src_size:
            ret = self._resize_dst_volume(dst_install_path, src_size, cmd.dstMonHostname, cmd.dstMonSshUsername, cmd.dstMonSshPassword, cmd.dstMonSshPort)
            if ret != 0:
                rsp.success = False
                rsp.error = "Failed to resize volume before migrate."
                return jsonobject.dumps(rsp)


        ret = self._migrate_volume_segment(cmd.parentUuid, cmd.resourceUuid, cmd.srcInstallPath, cmd.dstInstallPath, cmd.dstMonHostname, cmd.dstMonSshUsername, cmd.dstMonSshPassword, cmd.dstMonSshPort)
        if ret != 0:
            rsp.success = False
            rsp.error = "Failed to migrate volume segment from one ceph primary storage to another."
        self._set_capacity_to_response(rsp)
        return jsonobject.dumps(rsp)
Esempio n. 28
0
 def get_volume_size(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     path = self._normalize_install_path(cmd.installPath)
     rsp = GetVolumeSizeRsp()
     rsp.size = self._get_file_size(path)
     rsp.actualSize = self._get_file_actual_size(path)
     return jsonobject.dumps(rsp)
Esempio n. 29
0
    def download(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        pool, image_name = self._parse_install_path(cmd.installPath)
        tmp_image_name = 'tmp-%s' % image_name

        lichbd_file = os.path.join(pool, image_name)
        tmp_lichbd_file = os.path.join(pool, tmp_image_name)

        lichbd.lichbd_mkpool(os.path.dirname(lichbd_file))
        shell.call('set -o pipefail; wget --no-check-certificate -q -O - %s | lichbd import - %s -p lichbd' % (cmd.url, tmp_lichbd_file))

        @rollbackable
        def _1():
            if lichbd.lichbd_file_exist(tmp_lichbd_file):
                lichbd.lichbd_rm(tmp_lichbd_file)
            lichbd.lichbd_rm(lichbd_file)
        _1()

        qemu_img = lichbd.lichbd_get_qemu_img_path()
        file_format = shell.call("set -o pipefail;%s info rbd:%s/%s 2>/dev/null | grep 'file format' | cut -d ':' -f 2" % (qemu_img, pool, tmp_image_name))
        file_format = file_format.strip()
        if file_format not in ['qcow2', 'raw']:
            raise Exception('unknown image format: %s' % file_format)

        lichbd.lichbd_mv(lichbd_file, tmp_lichbd_file)
        size = lichbd.lichbd_file_size(lichbd_file)
        rsp = DownloadRsp()
        rsp.size = size
        self._set_capacity_to_response(rsp)
        return jsonobject.dumps(rsp)
Esempio n. 30
0
 def test_json(self):
     b = B()
     jstr = jsonobject.dumps(b)
     nb = jsonobject.loads(jstr)
     self.assertEqual(2, len(nb.cs))
     self.assertEqual(3, len(nb.lst))
     self.assertEqual(1, nb.lst[0])
     self.assertEqual('home', nb.address)
     self.assertEqual(19, nb.c.age)
     self.assertEqual('hello', nb.name)
     self.assertFalse(nb.male)
     jstr2 = jsonobject.dumps(nb)
     self.assertEqual(jstr, jstr2)
     jb = jsonobject.loads(jstr)
     print jb.xxxxx
     print jb.lst
Esempio n. 31
0
 def convert_qcow2_to_raw(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     return self.imagestore_client.convert_image_raw(cmd)
Esempio n. 32
0
 def check_bits(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     rsp = CheckBitsRsp()
     rsp.existing = os.path.exists(cmd.path)
     return jsonobject.dumps(rsp)
Esempio n. 33
0
 def get_physical_capacity(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     rsp = AgentResponse()
     rsp.totalCapacity, rsp.availableCapacity = self._get_disk_capacity(
         cmd.storagePath)
     return jsonobject.dumps(rsp)
Esempio n. 34
0
 def get_image_size(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     rsp = GetImageSizeRsp()
     rsp.size, rsp.actualSize = linux.qcow2_size_and_actual_size(
         cmd.installPath)
     return jsonobject.dumps(rsp)
Esempio n. 35
0
    def list(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = ListResponse()

        rsp.paths = kvmagent.listPath(cmd.path)
        return jsonobject.dumps(rsp)
Esempio n. 36
0
 def commit_to_imagestore(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     return self.imagestore_client.commit_to_imagestore(cmd, req)
Esempio n. 37
0
 def apply_userdata(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     self._apply_userdata_xtables(cmd.userdata)
     self._apply_userdata_vmdata(cmd.userdata)
     self._apply_userdata_restart_httpd(cmd.userdata)
     return jsonobject.dumps(ApplyUserdataRsp())
Esempio n. 38
0
    def start_collectd_exporter(self, req):
        @in_bash
        def start_exporter(cmd):
            conf_path = os.path.join(os.path.dirname(cmd.binaryPath),
                                     'collectd.conf')

            conf = '''Interval {{INTERVAL}}
FQDNLookup false

LoadPlugin syslog
LoadPlugin aggregation
LoadPlugin cpu
LoadPlugin disk
LoadPlugin interface
LoadPlugin memory
LoadPlugin network
LoadPlugin virt

<Plugin aggregation>
	<Aggregation>
		#Host "unspecified"
		Plugin "cpu"
		#PluginInstance "unspecified"
		Type "cpu"
		#TypeInstance "unspecified"

		GroupBy "Host"
		GroupBy "TypeInstance"

		CalculateNum false
		CalculateSum false
		CalculateAverage true
		CalculateMinimum false
		CalculateMaximum false
		CalculateStddev false
	</Aggregation>
</Plugin>

<Plugin cpu>
  ReportByCpu true
  ReportByState true
  ValuesPercentage true
</Plugin>

<Plugin disk>
  Disk "/^sd[a-z]$/"
  Disk "/^hd[a-z]$/"
  Disk "/^vd[a-z]$/"
  IgnoreSelected false
</Plugin>

<Plugin "interface">
{% for i in INTERFACES -%}
  Interface "{{i}}"
{% endfor -%}
  IgnoreSelected false
</Plugin>

<Plugin memory>
	ValuesAbsolute true
	ValuesPercentage false
</Plugin>

<Plugin virt>
	Connection "qemu:///system"
	RefreshInterval {{INTERVAL}}
	HostnameFormat name
    PluginInstanceFormat name
    BlockDevice "/:hd[a-z]/"
    IgnoreSelected true
</Plugin>

<Plugin network>
	Server "localhost" "25826"
</Plugin>

'''

            tmpt = Template(conf)
            conf = tmpt.render({
                'INTERVAL': cmd.interval,
                'INTERFACES': interfaces,
            })

            need_restart_collectd = False
            if os.path.exists(conf_path):
                with open(conf_path, 'r') as fd:
                    old_conf = fd.read()

                if old_conf != conf:
                    with open(conf_path, 'w') as fd:
                        fd.write(conf)
                    need_restart_collectd = True
            else:
                with open(conf_path, 'w') as fd:
                    fd.write(conf)
                need_restart_collectd = True

            cpid = linux.find_process_by_cmdline(['collectd', conf_path])
            mpid = linux.find_process_by_cmdline(['collectdmon', conf_path])

            if not cpid:
                bash_errorout('collectdmon -- -C %s' % conf_path)
            else:
                if need_restart_collectd:
                    if not mpid:
                        bash_errorout('kill -TERM %s' % cpid)
                        bash_errorout('collectdmon -- -C %s' % conf_path)
                    else:
                        bash_errorout('kill -HUP %s' % mpid)

            pid = linux.find_process_by_cmdline([cmd.binaryPath])
            if not pid:
                EXPORTER_PATH = cmd.binaryPath
                LOG_FILE = os.path.join(os.path.dirname(EXPORTER_PATH),
                                        cmd.binaryPath + '.log')
                ARGUMENTS = cmd.startupArguments
                if not ARGUMENTS:
                    ARGUMENTS = ""
                bash_errorout('chmod +x {{EXPORTER_PATH}}')
                bash_errorout(
                    "nohup {{EXPORTER_PATH}} {{ARGUMENTS}} >{{LOG_FILE}} 2>&1 < /dev/null &\ndisown"
                )

        para = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = kvmagent.AgentResponse()

        eths = bash_o("ls /sys/class/net").split()
        interfaces = []
        for eth in eths:
            eth = eth.strip(' \t\n\r')
            if eth == 'lo': continue
            if eth == 'bonding_masters': continue
            elif eth.startswith('vnic'): continue
            elif eth.startswith('outer'): continue
            elif eth.startswith('br_'): continue
            elif not eth: continue
            else:
                interfaces.append(eth)

        for cmd in para.cmds:
            start_exporter(cmd)

        self.install_iptables()

        return jsonobject.dumps(rsp)
Esempio n. 39
0
 def release_userdata(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     html_folder = os.path.join(self.USERDATA_ROOT, cmd.namespaceName,
                                'html', cmd.vmIp)
     shell.call('rm -rf %s' % html_folder)
     return jsonobject.dumps(ReleaseUserdataRsp())
Esempio n. 40
0
    def apply_dhcp(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])

        namespace_dhcp = {}
        for d in cmd.dhcp:
            lst = namespace_dhcp.get(d.namespaceName)
            if not lst:
                lst = []
                namespace_dhcp[d.namespaceName] = lst
            lst.append(d)

        @in_bash
        def apply(dhcp):
            bridge_name = dhcp[0].bridgeName
            namespace_name = dhcp[0].namespaceName
            conf_file_path, dhcp_path, dns_path, option_path, log_path = self._make_conf_path(
                namespace_name)

            conf_file = '''\
domain-needed
bogus-priv
no-hosts
addn-hosts={{dns}}
dhcp-option=vendor:MSFT,2,1i
dhcp-lease-max=65535
dhcp-hostsfile={{dhcp}}
dhcp-optsfile={{option}}
log-facility={{log}}
interface={{iface_name}}
except-interface=lo
bind-interfaces
leasefile-ro
{% for g in gateways -%}
dhcp-range={{g}},static
{% endfor -%}
'''

            br_num = shell.call(
                "ip netns list-id | grep -w %s | awk '{print $2}'" %
                namespace_name)
            br_num = br_num.strip(' \t\r\n')
            if not br_num:
                raise Exception('cannot find the ID for the namespace[%s]' %
                                namespace_name)

            tmpt = Template(conf_file)
            conf_file = tmpt.render({
                'dns':
                dns_path,
                'dhcp':
                dhcp_path,
                'option':
                option_path,
                'log':
                log_path,
                'iface_name':
                'inner%s' % br_num,
                'gateways': [d.gateway for d in dhcp if d.gateway]
            })

            restart_dnsmasq = cmd.rebuild
            if not os.path.exists(conf_file_path) or cmd.rebuild:
                with open(conf_file_path, 'w') as fd:
                    fd.write(conf_file)
            else:
                with open(conf_file_path, 'r') as fd:
                    c = fd.read()

                if c != conf_file:
                    logger.debug(
                        'dnsmasq configure file for bridge[%s] changed, restart it'
                        % bridge_name)
                    restart_dnsmasq = True
                    with open(conf_file_path, 'w') as fd:
                        fd.write(conf_file)
                    logger.debug(
                        'wrote dnsmasq configure file for bridge[%s]\n%s' %
                        (bridge_name, conf_file))

            info = []
            for d in dhcp:
                dhcp_info = {'tag': d.mac.replace(':', '')}
                dhcp_info.update(d.__dict__)
                dhcp_info['dns'] = ','.join(d.dns)
                routes = []
                # add classless-static-route (option 121) for gateway:
                if d.isDefaultL3Network:
                    routes.append(','.join(['0.0.0.0/0', d.gateway]))
                for route in d.hostRoutes:
                    routes.append(','.join([route.prefix, route.nexthop]))
                dhcp_info['routes'] = ','.join(routes)
                info.append(dhcp_info)

                if not cmd.rebuild:
                    self._erase_configurations(d.mac, d.ip, dhcp_path,
                                               dns_path, option_path)

            dhcp_conf = '''\
{% for d in dhcp -%}
{% if d.isDefaultL3Network -%}
{{d.mac}},set:{{d.tag}},{{d.ip}},{{d.hostname}},infinite
{% else -%}
{{d.mac}},set:{{d.tag}},{{d.ip}},infinite
{% endif -%}
{% endfor -%}
'''

            tmpt = Template(dhcp_conf)
            dhcp_conf = tmpt.render({'dhcp': info})
            mode = 'a+'
            if cmd.rebuild:
                mode = 'w'

            with open(dhcp_path, mode) as fd:
                fd.write(dhcp_conf)

            option_conf = '''\
{% for o in options -%}
{% if o.isDefaultL3Network -%}
{% if o.gateway -%}
tag:{{o.tag}},option:router,{{o.gateway}}
{% endif -%}
{% if o.dns -%}
tag:{{o.tag}},option:dns-server,{{o.dns}}
{% endif -%}
{% if o.dnsDomain -%}
tag:{{o.tag}},option:domain-name,{{o.dnsDomain}}
{% endif -%}
{% if o.routes -%}
tag:{{o.tag}},option:classless-static-route,{{o.routes}}
{% endif -%}
{% else -%}
tag:{{o.tag}},3
tag:{{o.tag}},6
{% endif -%}
tag:{{o.tag}},option:netmask,{{o.netmask}}
{% if o.mtu -%}
tag:{{o.tag}},option:mtu,{{o.mtu}}
{% endif -%}
{% endfor -%}
    '''
            tmpt = Template(option_conf)
            option_conf = tmpt.render({'options': info})

            with open(option_path, mode) as fd:
                fd.write(option_conf)

            hostname_conf = '''\
{% for h in hostnames -%}
{% if h.isDefaultL3Network and h.hostname -%}
{{h.ip}} {{h.hostname}}
{% endif -%}
{% endfor -%}
    '''
            tmpt = Template(hostname_conf)
            hostname_conf = tmpt.render({'hostnames': info})

            with open(dns_path, mode) as fd:
                fd.write(hostname_conf)

            if restart_dnsmasq:
                self._restart_dnsmasq(namespace_name, conf_file_path)
            else:
                self._refresh_dnsmasq(namespace_name, conf_file_path)

        for k, v in namespace_dhcp.iteritems():
            apply(v)

        rsp = ApplyDhcpRsp()
        return jsonobject.dumps(rsp)
Esempio n. 41
0
    def copy_bits_to_remote(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        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.uuid
        PFILE = shell.call('mktemp /tmp/tmp-XXXXXX').strip()

        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
            PASSWORD = cmd.dstPassword
            USER = cmd.dstUsername
            IP = cmd.dstIp
            PORT = (cmd.dstPort and cmd.dstPort or "22")
            DIR = os.path.dirname(path)

            if cmd.dstUsername == 'root':
                _, _, err = bash_progress_1(
                    # Fixes ZSTAC-13430: handle extremely complex password like ~ ` !@#$%^&*()_+-=[]{}|?<>;:'"/ .
                    'echo \'{{PASSWORD}}\' > /tmp/tmp_passwd && rsync -av --progress --relative {{PATH}} --rsh="/usr/bin/sshpass -f/tmp/tmp_passwd ssh -o StrictHostKeyChecking=no -p {{PORT}} -l {{USER}}" {{IP}}:/ 1>{{PFILE}}',
                    _get_progress,
                    False)

                if err:
                    raise Exception('fail to migrate vm to host, because %s' %
                                    str(err))
            else:
                raise Exception("cannot support migrate to non-root user host")
            written += os.path.getsize(path)
            bash_errorout(
                '/usr/bin/sshpass -f/tmp/tmp_passwd ssh -o StrictHostKeyChecking=no -p {{PORT}} {{USER}}@{{IP}} "/bin/sync {{PATH}}"'
            )
            percent = int(
                round(float(written) / float(total) * (end - start) + start))
            report.progress_report(percent, "report")

        if os.path.exists(PFILE):
            os.remove(PFILE)
        rsp = AgentResponse()
        rsp.totalCapacity, rsp.availableCapacity = self._get_disk_capacity(
            cmd.storagePath)
        return jsonobject.dumps(rsp)
Esempio n. 42
0
    def ping(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])

        facts = bash_o('ceph -s -f json')
        mon_facts = jsonobject.loads(facts)
        found = False
        for mon in mon_facts.monmap.mons:
            if cmd.monAddr in mon.addr:
                found = True
                break

        rsp = PingRsp()

        if not found:
            rsp.success = False
            rsp.failure = "MonAddrChanged"
            rsp.error = 'The mon addr is changed on the mon server[uuid:%s], not %s anymore.' \
                        'Reconnect the ceph primary storage' \
                        ' may solve this issue' % (cmd.monUuid, cmd.monAddr)
            return jsonobject.dumps(rsp)

        def retry(times=3, sleep_time=3):
            def wrap(f):
                @functools.wraps(f)
                def inner(*args, **kwargs):
                    for i in range(0, times):
                        try:
                            return f(*args, **kwargs)
                        except Exception as e:
                            logger.error(e)
                            time.sleep(sleep_time)
                    rsp.error = (
                        "Still failed after retry. Below is detail:\n %s" % e)

                return inner

            return wrap

        @retry()
        def doPing():
            # try to delete test file, ignore the result
            pool, objname = cmd.testImagePath.split('/')
            bash_r("rados -p '%s' rm '%s'" % (pool, objname))
            r, o, e = bash_roe(
                "echo zstack | timeout 60 rados -p '%s' put '%s' -" %
                (pool, objname))
            if r != 0:
                rsp.success = False
                rsp.failure = "UnableToCreateFile"
                if r == 124:
                    # timeout happened
                    rsp.error = 'failed to create heartbeat object on ceph, timeout after 60s, %s %s' % (
                        e, o)
                    raise Exception(rsp.error)
                else:
                    rsp.error = "%s %s" % (e, o)

        doPing()

        self._set_capacity_to_response(rsp)
        return jsonobject.dumps(rsp)
Esempio n. 43
0
    def setup_self_fencer(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])

        self.run_filesystem_fencer = True

        @thread.AsyncThread
        def heartbeat_file_fencer(heartbeat_file_path, ps_uuid):
            try:
                failure = 0

                while self.run_filesystem_fencer:
                    time.sleep(cmd.interval)

                    touch = shell.ShellCmd(
                        'timeout %s touch %s; exit $?' %
                        (cmd.storageCheckerTimeout, heartbeat_file_path))
                    touch(False)
                    if touch.return_code == 0:
                        failure = 0
                        continue

                    logger.warn(
                        'unable to touch %s, %s %s' %
                        (heartbeat_file_path, touch.stderr, touch.stdout))
                    failure += 1

                    if failure == cmd.maxAttempts:
                        logger.warn(
                            'failed to touch the heartbeat file[%s] %s times, we lost the connection to the storage,'
                            'shutdown ourselves' %
                            (heartbeat_file_path, cmd.maxAttempts))
                        mountPath = (os.path.split(heartbeat_file_path))[0]
                        self.report_storage_status([ps_uuid], 'Disconnected')
                        kill_vm(cmd.maxAttempts, mountPath, True)

                logger.debug('stop heartbeat[%s] for filesystem self-fencer' %
                             heartbeat_file_path)
            except:
                content = traceback.format_exc()
                logger.warn(content)

        gateway = cmd.storageGateway
        if not gateway:
            gateway = linux.get_gateway_by_default_route()

        @thread.AsyncThread
        def storage_gateway_fencer(gw):
            failure = 0

            try:
                while self.run_filesystem_fencer:
                    time.sleep(cmd.interval)

                    ping = shell.ShellCmd(
                        "nmap -sP -PI %s | grep 'Host is up'" % gw)
                    ping(False)
                    if ping.return_code == 0:
                        failure = 0
                        continue

                    logger.warn(
                        'unable to ping the storage gateway[%s], %s %s' %
                        (gw, ping.stderr, ping.stdout))
                    failure += 1

                    if failure == cmd.maxAttempts:
                        logger.warn(
                            'failed to ping storage gateway[%s] %s times, we lost connection to the storage,'
                            'shutdown ourselves' % (gw, cmd.maxAttempts))
                        self.report_storage_status(cmd.psUuids, 'Disconnected')
                        kill_vm(cmd.maxAttempts)

                logger.debug(
                    'stop gateway[%s] fencer for filesystem self-fencer' % gw)
            except:
                content = traceback.format_exc()
                logger.warn(content)

        for mount_point, uuid in zip(cmd.mountPoints, cmd.uuids):
            if not linux.timeout_isdir(mount_point):
                raise Exception('the mount point[%s] is not a directory' %
                                mount_point)

            hb_file = os.path.join(
                mount_point, 'heartbeat-file-kvm-host-%s.hb' % cmd.hostUuid)
            heartbeat_file_fencer(hb_file, uuid)

        if gateway:
            storage_gateway_fencer(gateway)
        else:
            logger.warn(
                'cannot find storage gateway, unable to setup storage gateway fencer'
            )

        return jsonobject.dumps(AgentRsp())
Esempio n. 44
0
 def write_image_metadata(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     meta_data = cmd.metaData
     self._write_image_metadata(meta_data.installPath, meta_data)
     rsp = WriteImageMetaDataResponse()
     return jsonobject.dumps(rsp)
Esempio n. 45
0
    def setup_self_fencer(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])

        @thread.AsyncThread
        def heartbeat_file_fencer(mount_path, ps_uuid, mounted_by_zstack):
            def try_remount_fs():
                if mount_path_is_nfs(mount_path):
                    shell.run("systemctl start nfs-client.target")

                while self.run_filesystem_fencer(ps_uuid, created_time):
                    if linux.is_mounted(
                            path=mount_path) and touch_heartbeat_file():
                        self.report_storage_status([ps_uuid], 'Connected')
                        logger.debug(
                            "fs[uuid:%s] is reachable again, report to management"
                            % ps_uuid)
                        break
                    try:
                        logger.debug(
                            'fs[uuid:%s] is unreachable, it will be remounted after 180s'
                            % ps_uuid)
                        time.sleep(180)
                        if not self.run_filesystem_fencer(
                                ps_uuid, created_time):
                            break
                        linux.remount(url, mount_path, options)
                        self.report_storage_status([ps_uuid], 'Connected')
                        logger.debug(
                            "remount fs[uuid:%s] success, report to management"
                            % ps_uuid)
                        break
                    except:
                        logger.warn(
                            'remount fs[uuid:%s] fail, try again soon' %
                            ps_uuid)
                        kill_progresses_using_mount_path(mount_path)

                logger.debug('stop remount fs[uuid:%s]' % ps_uuid)

            def after_kill_vm():
                if not killed_vm_pids or not mounted_by_zstack:
                    return

                try:
                    kill_and_umount(mount_path, mount_path_is_nfs(mount_path))
                except UmountException:
                    if shell.run('ps -p %s' % ' '.join(killed_vm_pids)) == 0:
                        virsh_list = shell.call(
                            "timeout 10 virsh list --all || echo 'cannot obtain virsh list'"
                        )
                        logger.debug("virsh_list:\n" + virsh_list)
                        logger.error(
                            'kill vm[pids:%s] failed because of unavailable fs[mountPath:%s].'
                            ' please retry "umount -f %s"' %
                            (killed_vm_pids, mount_path, mount_path))
                        return

                try_remount_fs()

            def touch_heartbeat_file():
                touch = shell.ShellCmd(
                    'timeout %s touch %s' %
                    (cmd.storageCheckerTimeout, heartbeat_file_path))
                touch(False)
                if touch.return_code != 0:
                    logger.warn(
                        'unable to touch %s, %s %s' %
                        (heartbeat_file_path, touch.stderr, touch.stdout))
                return touch.return_code == 0

            heartbeat_file_path = os.path.join(
                mount_path, 'heartbeat-file-kvm-host-%s.hb' % cmd.hostUuid)
            created_time = time.time()
            with self.fencer_lock:
                self.run_filesystem_fencer_timestamp[ps_uuid] = created_time
            try:
                failure = 0
                url = shell.call("mount | grep -e '%s' | awk '{print $1}'" %
                                 mount_path).strip()
                options = shell.call(
                    "mount | grep -e '%s' | awk -F '[()]' '{print $2}'" %
                    mount_path).strip()

                while self.run_filesystem_fencer(ps_uuid, created_time):
                    time.sleep(cmd.interval)
                    if touch_heartbeat_file():
                        failure = 0
                        continue

                    failure += 1
                    if failure == cmd.maxAttempts:
                        logger.warn(
                            'failed to touch the heartbeat file[%s] %s times, we lost the connection to the storage,'
                            'shutdown ourselves' %
                            (heartbeat_file_path, cmd.maxAttempts))
                        self.report_storage_status([ps_uuid], 'Disconnected')
                        killed_vm_pids = kill_vm(cmd.maxAttempts, [mount_path],
                                                 True)
                        after_kill_vm()

                logger.debug('stop heartbeat[%s] for filesystem self-fencer' %
                             heartbeat_file_path)

            except:
                content = traceback.format_exc()
                logger.warn(content)

        for mount_path, uuid, mounted_by_zstack in zip(cmd.mountPaths,
                                                       cmd.uuids,
                                                       cmd.mountedByZStack):
            if not linux.timeout_isdir(mount_path):
                raise Exception('the mount path[%s] is not a directory' %
                                mount_path)

            heartbeat_file_fencer(mount_path, uuid, mounted_by_zstack)

        return jsonobject.dumps(AgentRsp())
Esempio n. 46
0
 def _get_file_size(self, path):
     o = shell.call('rbd --format json info %s' % path)
     o = jsonobject.loads(o)
     return long(o.size_)
Esempio n. 47
0
 def callback(self, req):
     rsp = jsonobject.loads(req[http.REQUEST_BODY])
     print jsonobject.dumps(rsp)
Esempio n. 48
0
    def setup_ceph_self_fencer(self, req):
        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        mon_url = '\;'.join(cmd.monUrls)
        mon_url = mon_url.replace(':', '\\\:')

        self.run_ceph_fencer = True

        def ceph_in_error_stat():
            # HEALTH_OK,HEALTH_WARN,HEALTH_ERR and others...
            healthStatus = shell.call('ceph health')
            return not (healthStatus.startswith('HEALTH_OK')
                        or healthStatus.startswith('HEALTH_WARN'))

        def heartbeat_file_exists():
            touch = shell.ShellCmd(
                'timeout %s qemu-img info rbd:%s:id=zstack:key=%s:auth_supported=cephx\;none:mon_host=%s'
                % (cmd.storageCheckerTimeout, cmd.heartbeatImagePath,
                   cmd.userKey, mon_url))
            touch(False)

            if touch.return_code == 0:
                return True

            logger.warn('cannot query heartbeat image: %s: %s' %
                        (cmd.heartbeatImagePath, touch.stderr))
            return False

        def create_heartbeat_file():
            create = shell.ShellCmd(
                'timeout %s qemu-img create -f raw rbd:%s:id=zstack:key=%s:auth_supported=cephx\;none:mon_host=%s 1'
                % (cmd.storageCheckerTimeout, cmd.heartbeatImagePath,
                   cmd.userKey, mon_url))
            create(False)

            if create.return_code == 0 or "File exists" in create.stderr:
                return True

            logger.warn('cannot create heartbeat image: %s: %s' %
                        (cmd.heartbeatImagePath, create.stderr))
            return False

        def delete_heartbeat_file():
            delete = shell.ShellCmd(
                "timeout %s rbd rm --id zstack %s -m %s" %
                (cmd.storageCheckerTimeout, cmd.heartbeatImagePath, mon_url))
            delete(False)

        @thread.AsyncThread
        def heartbeat_on_ceph():
            try:
                failure = 0

                while self.run_ceph_fencer:
                    time.sleep(cmd.interval)

                    if heartbeat_file_exists() or create_heartbeat_file():
                        failure = 0
                        continue

                    failure += 1
                    if failure == cmd.maxAttempts:
                        # c.f. We discovered that, Ceph could behave the following:
                        #  1. Create heart-beat file, failed with 'File exists'
                        #  2. Query the hb file in step 1, and failed again with 'No such file or directory'
                        if ceph_in_error_stat():
                            path = (os.path.split(cmd.heartbeatImagePath))[0]
                            kill_vm(cmd.maxAttempts, path, False)
                        else:
                            delete_heartbeat_file()

                        # reset the failure count
                        failure = 0

                logger.debug('stop self-fencer on ceph primary storage')
            except:
                content = traceback.format_exc()
                logger.warn(content)

        heartbeat_on_ceph()

        return jsonobject.dumps(AgentRsp())
Esempio n. 49
0
 def rebase_root_volume_to_backing_file(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     linux.qcow2_rebase_no_check(cmd.backingFilePath, cmd.rootVolumePath)
     return jsonobject.dumps(AgentResponse())
Esempio n. 50
0
 def cancel_filesystem_self_fencer(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     with self.fencer_lock:
         for ps_uuid in cmd.psUuids:
             self.run_filesystem_fencer_timestamp.pop(ps_uuid, None)
     return jsonobject.dumps(AgentRsp())
Esempio n. 51
0
 def delete_target(self, req):
     cmd = jsonobject.loads(req[http.REQUEST_BODY])
     self._delete_target(cmd.target, cmd.uuid)
     logger.debug('deleted iscsi target[%s]' % cmd.target)
     rsp = AgentCapacityResponse()
     return jsonobject.dumps(rsp)
Esempio n. 52
0
    def download_image(self, req):
        #TODO: report percentage to mgmt server
        def percentage_callback(percent, url):
            logger.debug('Downloading %s ... %s%%' % (url, percent))

        def use_wget(url, name, workdir, timeout):
            return linux.wget(url,
                              workdir=workdir,
                              rename=name,
                              timeout=timeout,
                              interval=2,
                              callback=percentage_callback,
                              callback_data=url)

        cmd = jsonobject.loads(req[http.REQUEST_BODY])
        rsp = DownloadResponse()
        supported_schemes = [self.URL_HTTP, self.URL_HTTPS, self.URL_FILE]
        if cmd.urlScheme not in supported_schemes:
            rsp.success = False
            rsp.error = 'unsupported url scheme[%s], SimpleSftpBackupStorage only supports %s' % (
                cmd.urlScheme, supported_schemes)
            return jsonobject.dumps(rsp)

        path = os.path.dirname(cmd.installPath)
        if not os.path.exists(path):
            os.makedirs(path, 0777)
        image_name = os.path.basename(cmd.installPath)
        install_path = cmd.installPath

        timeout = cmd.timeout if cmd.timeout else 7200
        if cmd.urlScheme in [self.URL_HTTP, self.URL_HTTPS]:
            try:
                ret = use_wget(cmd.url, image_name, path, timeout)
                if ret != 0:
                    rsp.success = False
                    rsp.error = 'http/https download failed, [wget -O %s %s] returns value %s' % (
                        image_name, cmd.url, ret)
                    return jsonobject.dumps(rsp)
            except linux.LinuxError as e:
                traceback.format_exc()
                rsp.success = False
                rsp.error = str(e)
                return jsonobject.dumps(rsp)
        elif cmd.urlScheme == self.URL_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)

            shell.call('yes | cp %s %s' % (src_path, install_path))

        os.chmod(cmd.installPath, stat.S_IRUSR + stat.S_IRGRP + stat.S_IROTH)

        size = os.path.getsize(install_path)
        image_format = bash_o(
            "qemu-img info %s | grep -w '^file format' | awk '{print $3}'" %
            install_path).strip('\n')
        if "raw" in image_format:
            if "ISO" in bash_o("file %s" % install_path):
                image_format = "iso"
        md5sum = 'not calculated'
        logger.debug('successfully downloaded %s to %s' %
                     (cmd.url, install_path))
        (total, avail) = self.get_capacity()
        rsp.md5Sum = md5sum
        rsp.actualSize = size
        rsp.size = linux.qcow2_virtualsize(install_path)
        rsp.totalCapacity = total
        rsp.availableCapacity = avail
        rsp.format = image_format
        return jsonobject.dumps(rsp)