Пример #1
0
def get_os_mount_tools(os_type, connection_info, event_manager,
                       ignore_devices):
    os_mount_tools = {constants.OS_TYPE_LINUX: [ubuntu.UbuntuOSMountTools],
                      constants.OS_TYPE_WINDOWS: [windows.WindowsMountTools]}

    if os_type and os_type not in os_mount_tools:
        raise exception.MigrationToolException("Unsupported OS type: %s" % os_type)

    for cls in os_mount_tools.get(os_type,
                                  itertools.chain(*os_mount_tools.values())):
        tools = cls(connection_info, event_manager, ignore_devices)
        LOG.debug("Testing OS mount tools: %s", cls.__name__)
        if tools.check_os():
            return tools
    raise exception.MigrationToolException("OS mount tools not found")
Пример #2
0
 def _wait_for_task(self, task):
     while task.info.state not in [
             vim.TaskInfo.State.success, vim.TaskInfo.State.error
     ]:
         time.sleep(.1)
     if task.info.state == vim.TaskInfo.State.error:
         raise exception.MigrationToolException(task.info.error.msg)
Пример #3
0
    def _exec_task_process(self, ctxt, task_id, task_type, origin, destination,
                           instance, task_info):
        mp_ctx = multiprocessing.get_context('spawn')
        mp_q = mp_ctx.Queue()
        mp_log_q = mp_ctx.Queue()
        p = mp_ctx.Process(
            target=_task_process,
            args=(ctxt, task_id, task_type, origin, destination, instance,
                  task_info, mp_q, mp_log_q))

        p.start()
        LOG.info("Task process started: %s", task_id)
        self._rpc_conductor_client.set_task_host(
            ctxt, task_id, self._server, p.pid)

        self._handle_mp_log_events(p, mp_log_q)
        p.join()

        if mp_q.empty():
            raise exception.MigrationToolException("Task canceled")
        result = mp_q.get(False)

        if isinstance(result, str):
            raise exception.TaskProcessException(result)
        return result
Пример #4
0
def db_sync(engine, version=None):
    """Migrate the database to `version` or the most recent version."""
    if version is not None and int(version) < db_version(engine):
        raise exception.MigrationToolException(
            _("Cannot migrate to lower schema version."))

    return migration.db_sync(engine, version=version)
Пример #5
0
 def _wait_for_instance_deletion(self, instance_id):
     instances = self._nova.servers.findall(id=instance_id)
     while instances and instances[0].status != 'ERROR':
         time.sleep(2)
         instances = self._nova.servers.findall(id=instance_id)
     if instances:
         raise exception.MigrationToolException("VM is in status: %s" %
                                                instances[0].status)
Пример #6
0
 def _wait_for_image(self, nova, image, expected_status='ACTIVE'):
     image = nova.images.get(image.id)
     while image.status not in [expected_status, 'ERROR']:
         time.sleep(2)
         image = nova.images.get(image.id)
     if image.status != expected_status:
         raise exception.MigrationToolException("Image is in status: %s" %
                                                image.status)
Пример #7
0
 def _wait_for_instance(self, nova, instance, expected_status='ACTIVE'):
     instance = nova.servers.get(instance.id)
     while instance.status not in [expected_status, 'ERROR']:
         time.sleep(2)
         instance = nova.servers.get(instance.id)
     if instance.status != expected_status:
         raise exception.MigrationToolException("VM is in status: %s" %
                                                instance.status)
Пример #8
0
def exec_process(args):
    p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    std_out, std_err = p.communicate()
    if p.returncode:
        raise exception.MigrationToolException(
            "Command \"%s\" failed with exit code: %s\nstdout: %s\nstd_err: %s"
            % (args, p.returncode, std_out, std_err))
    return std_out
Пример #9
0
 def _wait_for_volume(self, nova, volume, expected_status='in-use'):
     volume = nova.volumes.findall(id=volume.id)[0]
     while volume.status not in [expected_status, 'error']:
         time.sleep(2)
         volume = nova.volumes.get(volume.id)
     if volume.status != expected_status:
         raise exception.MigrationToolException("Volume is in status: %s" %
                                                volume.status)
Пример #10
0
def wait_for_port_connectivity(address, port, max_wait=300):
    i = 0
    while not _check_port_open(address, port) and i < max_wait:
        time.sleep(1)
        i += 1
    if i == max_wait:
        raise exception.MigrationToolException("Connection failed on port %s" %
                                          port)
Пример #11
0
 def _get_default_cloud_user(self):
     cloud_cfg_path = os.path.join(self._os_root_dir, 'etc/cloud/cloud.cfg')
     if not self._test_path(cloud_cfg_path):
         raise exception.MigrationToolException(
             "cloud-init config file not found: %s" % cloud_cfg_path)
     cloud_cfg_content = self._read_file(cloud_cfg_path)
     cloud_cfg = yaml.load(cloud_cfg_content)
     return cloud_cfg.get('system_info',
                          {}).get('default_user',
                                  {}).get('name', DEFAULT_CLOUD_USER)
Пример #12
0
    def _export_disks(self, vm, export_path, context):
        disk_paths = []
        lease = vm.ExportVm()
        while True:
            if lease.state == vim.HttpNfcLease.State.ready:
                try:
                    tot_downloaded_bytes = 0
                    for du in [du for du in lease.info.deviceUrl if du.disk]:
                        # Key format: '/vm-70/VirtualLsiLogicController0:0'
                        ctrl_str, address = du.key[du.key.rindex('/') +
                                                   1:].split(':')

                        def _get_class_name(obj):
                            return obj.__class__.__name__.split('.')[-1]

                        for i, ctrl in enumerate([
                                d for d in vm.config.hardware.device if
                                isinstance(d, vim.vm.device.VirtualController)
                                and ctrl_str.startswith(_get_class_name(d))
                        ]):
                            if int(ctrl_str[len(_get_class_name(ctrl)):]) == i:
                                disk_key = [
                                    d for d in vm.config.hardware.device
                                    if isinstance(d, vim.vm.device.VirtualDisk)
                                    and d.controllerKey == ctrl.key
                                    and d.unitNumber == int(address)
                                ][0].key
                                break

                        response = request.urlopen(du.url, context=context)
                        path = os.path.join(export_path, du.targetId)
                        disk_paths.append({'path': path, 'id': disk_key})

                        LOG.info("Downloading: %s" % path)
                        with open(path, 'wb') as f:
                            while True:
                                chunk = response.read(1024 * 1024)
                                if not chunk:
                                    break
                                tot_downloaded_bytes += len(chunk)
                                f.write(chunk)
                                lease.HttpNfcLeaseProgress(
                                    int(tot_downloaded_bytes * 100 /
                                        (lease.info.totalDiskCapacityInKB *
                                         1024)))

                    lease.HttpNfcLeaseComplete()
                    return disk_paths
                except:
                    lease.HttpNfcLeaseAbort()
                    raise
            elif lease.state == vim.HttpNfcLease.State.error:
                raise exception.MigrationToolException(lease.error.msg)
            else:
                time.sleep(.1)
Пример #13
0
    def exec_command(self, cmd, args=[]):
        LOG.debug("Executing WSMAN command: %s", str([cmd] + args))
        std_out, std_err, exit_code = self._exec_command(cmd, args)

        if exit_code:
            raise exception.MigrationToolException(
                "Command \"%s\" failed with exit code: %s\n"
                "stdout: %s\nstd_err: %s" %
                (str([cmd] + args), exit_code, std_out, std_err))

        return std_out
Пример #14
0
def exec_ssh_cmd(ssh, cmd):
    LOG.debug("Executing SSH command: %s", cmd)
    stdin, stdout, stderr = ssh.exec_command(cmd)
    exit_code = stdout.channel.recv_exit_status()
    std_out = stdout.read()
    std_err = stderr.read()
    if exit_code:
        raise exception.MigrationToolException(
            "Command \"%s\" failed with exit code: %s\n"
            "stdout: %s\nstd_err: %s" %
            (cmd, exit_code, std_out, std_err))
    return std_out
Пример #15
0
    def _validate_create_body(self, body):
        migration = body["migration"]

        origin = migration["origin"]
        destination = migration["destination"]

        export_provider = factory.get_provider(origin["type"],
                                               constants.PROVIDER_TYPE_EXPORT,
                                               None)
        if not export_provider.validate_connection_info(
                origin.get("connection_info", {})):
            # TODO: use a decent exception
            raise exception.MigrationToolException("Invalid connection info")

        import_provider = factory.get_provider(destination["type"],
                                               constants.PROVIDER_TYPE_IMPORT,
                                               None)
        if not import_provider.validate_connection_info(
                destination.get("connection_info", {})):
            # TODO: use a decent exception
            raise exception.MigrationToolException("Invalid connection info")

        return origin, destination, migration["instances"]
Пример #16
0
    def __init__(self, ext_mgr=None):
        if ext_mgr is None:
            if self.ExtensionManager:
                ext_mgr = self.ExtensionManager()
            else:
                raise exception.MigrationToolException(
                    _("Must specify an ExtensionManager class"))

        mapper = ProjectMapper()
        self.resources = {}
        self._setup_routes(mapper, ext_mgr)
        self._setup_ext_routes(mapper, ext_mgr)
        self._setup_extensions(ext_mgr)
        super(APIRouter, self).__init__(mapper)
Пример #17
0
def get_os_morphing_tools(conn, os_type, os_root_dir, target_hypervisor,
                          target_platform, event_manager):
    os_morphing_tools_clss = {
        constants.OS_TYPE_LINUX: [debian.DebianMorphingTools,
                                  ubuntu.UbuntuMorphingTools,
                                  oracle.OracleMorphingTools,
                                  redhat.RedHatMorphingTools,
                                  suse.SUSEMorphingTools],
        constants.OS_TYPE_WINDOWS: [windows.WindowsMorphingTools],
        }

    if os_type and os_type not in os_morphing_tools_clss:
        raise exception.MigrationToolException("Unsupported OS type: %s" % os_type)

    for cls in os_morphing_tools_clss.get(
            os_type, itertools.chain(*os_morphing_tools_clss.values())):
        tools = cls(conn, os_root_dir, target_hypervisor, target_platform,
                    event_manager)
        LOG.debug("Testing OS morphing tools: %s", cls.__name__)
        os_info = tools.check_os()
        if os_info:
            return (tools, os_info)
    raise exception.MigrationToolException(
        "Cannot find the morphing tools for this OS image")
Пример #18
0
def create_keystone_session(ctxt, connection_info={}):
    keystone_version = connection_info.get("identity_api_version",
                                           CONF.keystone.identity_api_version)
    auth_url = connection_info.get("auth_url", CONF.keystone.auth_url)

    if not auth_url:
        raise exception.MigrationToolException(
            '"auth_url" not provided in "connection_info" and option '
            '"auth_url" in group "[openstack_migration_provider]" '
            'not set')

    username = connection_info.get("username")
    password = connection_info.get("password")
    project_name = connection_info.get("project_name", ctxt.project_name)
    project_domain_name = connection_info.get("project_domain_name",
                                              ctxt.project_domain)
    user_domain_name = connection_info.get("user_domain_name",
                                           ctxt.user_domain)
    allow_untrusted = connection_info.get("allow_untrusted",
                                          CONF.keystone.allow_untrusted)

    # TODO: add "ca_cert" to connection_info
    verify = not allow_untrusted

    plugin_args = {
        "auth_url": auth_url,
        "project_name": project_name,
    }

    if username:
        plugin_name = "password"
        plugin_args["username"] = username
        plugin_args["password"] = password
    else:
        plugin_name = "token"
        plugin_args["token"] = ctxt.auth_token

    if keystone_version == 3:
        plugin_name = "v3" + plugin_name
        plugin_args["project_domain_name"] = project_domain_name
        if username:
            plugin_args["user_domain_name"] = user_domain_name

    loader = loading.get_plugin_loader(plugin_name)
    auth = loader.load_from_options(**plugin_args)

    return session.Session(auth=auth, verify=verify)
Пример #19
0
    def _get_image_version_info(self):
        key_name = str(uuid.uuid4())

        self._load_registry_hive(
            "HKLM\%s" % key_name,
            "%sWindows\\System32\\config\\SOFTWARE" % self._os_root_dir)
        try:
            version_info_str = self._conn.exec_ps_command(
                "Get-ItemProperty "
                "'HKLM:\%s\Microsoft\Windows NT\CurrentVersion' "
                "| select CurrentVersion, CurrentMajorVersionNumber, "
                "CurrentMinorVersionNumber,  CurrentBuildNumber, "
                "InstallationType, ProductName, EditionID | FL" %
                key_name).replace(self._conn.EOL, os.linesep)
        finally:
            self._unload_registry_hive("HKLM\%s" % key_name)

        version_info = {}
        for n in [
                "CurrentVersion", "CurrentMajorVersionNumber",
                "CurrentMinorVersionNumber", "CurrentBuildNumber",
                "InstallationType", "ProductName", "EditionID"
        ]:
            version_info[n] = self._get_ps_fl_value(version_info_str, n)

        if (not version_info["CurrentMajorVersionNumber"]
                and not version_info["CurrentVersion"]):
            raise exception.MigrationToolException(
                "Cannot find Windows version info")

        if version_info["CurrentMajorVersionNumber"]:
            version_str = "%s.%s.%s" % (
                version_info["CurrentMajorVersionNumber"],
                version_info["CurrentMinorVersionNumber"],
                version_info["CurrentBuildNumber"])
        else:
            version_str = "%s.%s" % (version_info["CurrentVersion"],
                                     version_info["CurrentBuildNumber"])

        return (version.LooseVersion(version_str), version_info["EditionID"],
                version_info["InstallationType"], version_info["ProductName"])
Пример #20
0
    def import_instance(self, ctxt, connection_info, target_environment,
                        instance_name, export_info):
        session = keystone.create_keystone_session(ctxt, connection_info)

        glance_api_version = connection_info.get("image_api_version",
                                                 GLANCE_API_VERSION)

        nova = nova_client.Client(NOVA_API_VERSION, session=session)
        glance = glance_client.Client(glance_api_version, session=session)
        neutron = neutron_client.Client(NEUTRON_API_VERSION, session=session)
        cinder = cinder_client.Client(CINDER_API_VERSION, session=session)

        os_type = export_info.get('os_type')
        LOG.info("os_type: %s", os_type)

        glance_upload = target_environment.get(
            "glance_upload", CONF.openstack_migration_provider.glance_upload)
        target_disk_format = target_environment.get(
            "disk_format", CONF.openstack_migration_provider.disk_format)
        container_format = target_environment.get(
            "container_format",
            CONF.openstack_migration_provider.container_format)
        hypervisor_type = target_environment.get(
            "hypervisor_type",
            CONF.openstack_migration_provider.hypervisor_type)
        fip_pool_name = target_environment.get(
            "fip_pool_name", CONF.openstack_migration_provider.fip_pool_name)
        network_map = target_environment.get("network_map", {})
        keypair_name = target_environment.get("keypair_name")

        migr_image_name = target_environment.get(
            "migr_image_name",
            target_environment.get("migr_image_name_map", {}).get(
                os_type,
                CONF.openstack_migration_provider.migr_image_name_map.get(
                    os_type)))
        migr_flavor_name = target_environment.get(
            "migr_flavor_name",
            CONF.openstack_migration_provider.migr_flavor_name)

        migr_fip_pool_name = target_environment.get(
            "migr_fip_pool_name", fip_pool_name
            or CONF.openstack_migration_provider.fip_pool_name)
        migr_network_name = target_environment.get(
            "migr_network_name",
            CONF.openstack_migration_provider.migr_network_name)

        flavor_name = target_environment.get("flavor_name", migr_flavor_name)

        if not migr_image_name:
            raise exception.MigrationToolException(
                "No matching migration image type found")

        LOG.info("Migration image name: %s", migr_image_name)

        if not migr_network_name:
            if len(network_map) != 1:
                raise exception.MigrationToolException(
                    'If "migr_network_name" is not provided, "network_map" '
                    'must contain exactly one entry')
            migr_network_name = network_map.values()[0]

        disks_info = export_info["devices"]["disks"]

        images = []
        volumes = []
        ports = []

        try:
            if glance_upload:
                for disk_info in disks_info:
                    disk_path = disk_info["path"]
                    disk_file_info = utils.get_disk_info(disk_path)

                    # if target_disk_format == disk_file_info["format"]:
                    #    target_disk_path = disk_path
                    # else:
                    #    target_disk_path = (
                    #        "%s.%s" % (os.path.splitext(disk_path)[0],
                    #                   target_disk_format))
                    #    utils.convert_disk_format(disk_path, target_disk_path,
                    #                              target_disk_format)

                    self._event_manager.progress_update(
                        "Uploading Glance image")

                    disk_format = disk_file_info["format"]
                    image = self._create_image(glance, self._get_unique_name(),
                                               disk_path, disk_format,
                                               container_format,
                                               hypervisor_type)
                    images.append(image)

                    self._event_manager.progress_update(
                        "Waiting for Glance image to become active")
                    self._wait_for_image(nova, image)

                    virtual_disk_size = disk_file_info["virtual-size"]
                    if disk_format != constants.DISK_FORMAT_RAW:
                        virtual_disk_size += DISK_HEADER_SIZE

                    self._event_manager.progress_update(
                        "Creating Cinder volume")

                    volume_size_gb = math.ceil(virtual_disk_size / units.Gi)
                    volume = nova.volumes.create(
                        size=volume_size_gb,
                        display_name=self._get_unique_name(),
                        imageRef=image.id)
                    volumes.append(volume)

            migr_resources = self._deploy_migration_resources(
                nova, glance, neutron, os_type, migr_image_name,
                migr_flavor_name, migr_network_name, migr_fip_pool_name)

            nics_info = export_info["devices"].get("nics", [])

            try:
                for i, volume in enumerate(volumes):
                    self._wait_for_volume(nova, volume, 'available')

                    self._event_manager.progress_update(
                        "Attaching volume to worker instance")

                    self._attach_volume(nova, migr_resources.get_instance(),
                                        volume)

                    conn_info = migr_resources.get_guest_connection_info()

                osmorphing_hv_type = self._get_osmorphing_hypervisor_type(
                    hypervisor_type)

                self._event_manager.progress_update(
                    "Preparing instance for target platform")
                osmorphing_manager.morph_image(conn_info, os_type,
                                               osmorphing_hv_type,
                                               constants.PLATFORM_OPENSTACK,
                                               nics_info, self._event_manager)
            finally:
                self._event_manager.progress_update(
                    "Removing worker instance resources")
                migr_resources.delete()

            self._event_manager.progress_update("Renaming volumes")

            for i, volume in enumerate(volumes):
                new_volume_name = "%s %s" % (instance_name, i + 1)
                cinder.volumes.update(volume.id, name=new_volume_name)

            for nic_info in nics_info:
                self._event_manager.progress_update(
                    "Creating Neutron port for migrated instance")

                origin_network_name = nic_info.get("network_name")
                if not origin_network_name:
                    self._warn(
                        "Origin network name not provided for for nic: "
                        "%s, skipping", nic_info.get("name"))
                    continue

                network_name = network_map.get(origin_network_name)
                if not network_name:
                    raise exception.MigrationToolException(
                        "Network not mapped in network_map: %s" %
                        origin_network_name)

                ports.append(
                    self._create_neutron_port(neutron, network_name,
                                              nic_info.get("mac_address")))

            self._event_manager.progress_update("Creating migrated instance")

            self._create_target_instance(nova, flavor_name, instance_name,
                                         keypair_name, ports, volumes)
        except Exception:
            self._event_manager.progress_update("Deleting volumes")
            for volume in volumes:

                @utils.ignore_exceptions
                @utils.retry_on_error()
                def _del_volume():
                    volume.delete()

                _del_volume()
            self._event_manager.progress_update("Deleting Neutron ports")
            for port in ports:

                @utils.ignore_exceptions
                @utils.retry_on_error()
                def _del_port():
                    neutron.delete_port(port["id"])

                _del_port()
            raise
        finally:
            self._event_manager.progress_update("Deleting Glance images")
            for image in images:

                @utils.ignore_exceptions
                @utils.retry_on_error()
                def _del_image():
                    image.delete()

                _del_image()
Пример #21
0
    def _deploy_migration_resources(self, nova, glance, neutron, os_type,
                                    migr_image_name, migr_flavor_name,
                                    migr_network_name, migr_fip_pool_name):
        if not glance.images.findall(name=migr_image_name):
            raise exception.MigrationToolException(
                "Glance image \"%s\" not found" % migr_image_name)

        image = nova.images.find(name=migr_image_name)
        flavor = nova.flavors.find(name=migr_flavor_name)

        keypair = None
        instance = None
        floating_ip = None
        sec_group = None
        port = None

        try:
            migr_keypair_name = self._get_unique_name()

            self._event_manager.progress_update(
                "Creating migration worker instance keypair")

            k = paramiko.RSAKey.generate(2048)
            public_key = "ssh-rsa %s tmp@migration" % k.get_base64()
            keypair = self._create_keypair(nova, migr_keypair_name, public_key)

            self._event_manager.progress_update(
                "Creating migration worker instance Neutron port")

            port = self._create_neutron_port(neutron, migr_network_name)

            # TODO(alexpilotti): use a single username
            if os_type == constants.OS_TYPE_WINDOWS:
                username = MIGR_GUEST_USERNAME_WINDOWS
            else:
                username = MIGR_GUEST_USERNAME

            userdata = MIGR_USER_DATA % (username, public_key)
            instance = nova.servers.create(name=self._get_unique_name(),
                                           image=image,
                                           flavor=flavor,
                                           key_name=migr_keypair_name,
                                           userdata=userdata,
                                           nics=[{
                                               'port-id': port['id']
                                           }])

            self._event_manager.progress_update(
                "Adding migration worker instance floating IP")

            floating_ip = nova.floating_ips.create(pool=migr_fip_pool_name)
            self._wait_for_instance(nova, instance, 'ACTIVE')

            LOG.info("Floating IP: %s", floating_ip.ip)
            instance.add_floating_ip(floating_ip)

            self._event_manager.progress_update(
                "Adding migration worker instance security group")

            if os_type == constants.OS_TYPE_WINDOWS:
                guest_port = WINRM_HTTPS_PORT
            else:
                guest_port = SSH_PORT

            migr_sec_group_name = self._get_unique_name()
            sec_group = nova.security_groups.create(
                name=migr_sec_group_name, description=migr_sec_group_name)
            nova.security_group_rules.create(sec_group.id,
                                             ip_protocol="tcp",
                                             from_port=guest_port,
                                             to_port=guest_port)
            instance.add_security_group(sec_group.id)

            self._event_manager.progress_update(
                "Waiting for connectivity on host: %(ip)s:%(port)s" % {
                    "ip": floating_ip.ip,
                    "port": guest_port
                })

            utils.wait_for_port_connectivity(floating_ip.ip, guest_port)

            if os_type == constants.OS_TYPE_WINDOWS:
                password = self._get_instance_password(instance, k)
            else:
                password = None

            return _MigrationResources(nova, neutron, keypair, instance, port,
                                       floating_ip, guest_port, sec_group,
                                       username, password, k)
        except:
            if instance:
                nova.servers.delete(instance)
            if floating_ip:
                nova.floating_ips.delete(floating_ip)
            if port:
                neutron.delete_port(port['id'])
            if sec_group:
                nova.security_groups.delete(sec_group.id)
            if keypair:
                nova.keypairs.delete(keypair.name)
            raise