def test_update_migration_record(self): db_handler.initialise_flavor_mapping() flavor_data = {'src_flavor_name': "name", 'src_uuid': "123", 'src_cloud': "cloud1", 'dst_flavor_name': "d_name", 'dst_uuid': "234", 'dst_cloud': "cloud2", 'state': "unknown"} db_handler.record_flavor_migrated([flavor_data]) new_data = {'src_flavor_name': "name", 'src_uuid': "123", 'src_cloud': "cloud1", 'dst_flavor_name': "d_name", 'dst_uuid': "234", 'dst_cloud': "cloud2", 'state': "completed"} values = ["name", "123", "cloud1", "cloud2"] db_handler.update_migration_record(**new_data) return_data = db_handler.get_migrated_flavor(values) self.assertIsNot(None, return_data) self.assertEqual("completed", return_data["state"]) table_name = "flavors" w_dict = OrderedDict([('src_flavor_name', "name"), ('src_uuid', "123"), ('src_cloud', "cloud1"), ('dst_cloud', "cloud2")]) delete_record(table_name, w_dict)
def revert(self, flavors_to_migrate): if flavors_to_migrate is None: LOG.info("Start reverting flavours.\n") flavors_to_migrate = [] for flavor in self.nv_source.flavors.list(): flavors_to_migrate.append(flavor.name) elif type(flavors_to_migrate) is str: flavors_to_migrate = [flavors_to_migrate] elif type(flavors_to_migrate) is list \ and len(flavors_to_migrate) == 0: LOG.info("No flavour resources to be reverted.\n") return elif type(flavors_to_migrate) is list \ and len(flavors_to_migrate) > 0: LOG.info("Start reverting flavours.\n") else: LOG.error("Incorrect parameter '{0}'.\n" "Expects: a list of flavor names\n" "Received: '{1}'".format("tenants_to_move", flavors_to_migrate)) return s_cloud_name = cfg.CONF.SOURCE.os_cloud_name t_cloud_name = cfg.CONF.TARGET.os_cloud_name for flavor in flavors_to_migrate: try: s_flavor = self.nv_source.flavors.find(name=flavor) except nova_exceptions.NotFound: LOG.info("Do not need to revert flavour {0}, " "since it does not exist in cloud {1} " .format(flavor, s_cloud_name)) return values = [flavor, s_flavor.id, s_cloud_name, t_cloud_name] flavor_data = flavors.get_migrated_flavor(values) if flavor_data is not None: if flavor_data["state"] == "completed": try: t_flavor = self.nv_target.flavors.\ find(name=flavor_data["dst_flavor_name"]) self.nv_target.flavors.delete(t_flavor) except nova_exceptions.NotFound: LOG.info("Do not need to revert flavour {0}, " "since it has not been migrated successfully" " to cloud {1}" .format(flavor_data["dst_flavor_name"], t_cloud_name)) # delete record from flyway database flavors.delete_migration_record(values)
def test_delete_migration_record(self): db_handler.initialise_flavor_mapping() flavor_data = {'src_flavor_name': "name", 'src_uuid': "123", 'src_cloud': "cloud1", 'dst_flavor_name': "d_name", 'dst_uuid': "234", 'dst_cloud': "cloud2", 'state': "unknown"} db_handler.record_flavor_migrated([flavor_data]) values = ["name", "123", "cloud1", "cloud2"] db_handler.delete_migration_record(values) return_data = db_handler.get_migrated_flavor(values) self.assertIs(None, return_data)
def retrieve_flavor(self, server, s_nova_client, t_nova_client): """ function to safely get the corresponding flavor on destination cloud of the flavor which has been used by the server instance on source cloud :param server: server instance to migrate :param s_nova_client: nova client of the source cloud :param t_nova_client: nova client of the target cloud :return: flavor to be used by the server instance on destination :raise exceptions.ResourceNotFoundException: """ # find original flavor used on the source cloud s_flavor_id = server.flavor['id'] try: s_flavor = s_nova_client.flavors.find(id=s_flavor_id) except nova_exceptions.NotFound: raise exceptions.ResourceNotFoundException( ResourceType.vm, s_flavor_id, self.s_cloud_name) # check whether the flavor has been migrated or not filter_values = [s_flavor.name, s_flavor.id, self.s_cloud_name, self.t_cloud_name] m_flavor = flavors.get_migrated_flavor(filter_values) if not m_flavor: LOG.info("Flavor '{0}' required by instance [ID: {1}, Name: {2}] " "has been migrated yet, use default solution." .format(s_flavor.name, server.id, server.name)) try: m_flavor = t_nova_client.flavors.find(id=server.flavor['id']) except Exception: return None return m_flavor # try to get its corresponding flavor on destination cloud dst_flavor_id = m_flavor['dst_uuid'] try: flavor_to_use = t_nova_client.flavors.find(id=dst_flavor_id) except nova_exceptions.NotFound: LOG.info("Migrated flavor '{0}' does not exist on destination " "cloud '{1}'".format(s_flavor.name, self.t_cloud_name)) raise exceptions.ResourceNotFoundException( ResourceType.vm, s_flavor_id, self.s_cloud_name) return flavor_to_use
def migrate_one_flavor(self, flavor_name): try: s_flavor = self.nv_source.flavors.find(name=flavor_name) except nova_exceptions.NotFound: # encapsulate exceptions to make it more understandable # to user. Other exception handling mechanism can be added later raise exceptions.ResourceNotFoundException( ResourceType.flavor, flavor_name, cfg.CONF.SOURCE.os_cloud_name) s_cloud_name = cfg.CONF.SOURCE.os_cloud_name t_cloud_name = cfg.CONF.TARGET.os_cloud_name # check whether the tenant has been migrated values = [s_flavor, s_flavor.id, s_cloud_name, t_cloud_name] m_flavor = flavors.get_migrated_flavor(values) if m_flavor is not None and m_flavor['state'] == "completed": print("flavor {0} in cloud {1} has already been migrated" .format(m_flavor["src_flavor_name"], s_cloud_name)) elif m_flavor is None: # check for tenant name duplication new_flavor_name = s_flavor.name try: found = True while found: found = self.nv_target.flavors.find(name=new_flavor_name) if found: user_input = \ raw_input("duplicated flavor '{0}' found on " "cloud '{1}'\nPlease type in a new " "name or 'abort':" .format(found.name, t_cloud_name)) if user_input is "abort": # TODO: implement cleaning up and proper exit return None elif user_input: new_flavor_name = user_input except nova_exceptions.NotFound: # irrelevant exception swallow the exception pass new_flavor_details = { 'name': new_flavor_name, 'ram': s_flavor.ram, 'vcpus': s_flavor.vcpus, 'disk': s_flavor.disk, 'ephemeral': s_flavor.ephemera if s_flavor.ephemeral else 0, 'swap': s_flavor.swap if s_flavor.swap else 0, 'rxtx_factor': s_flavor.rxtx_factor, 'is_public': getattr(s_flavor, 'os-flavor-access:is_public', False)} # preparing record for inserting into database flavor_migration_data = {'src_flavor_name': s_flavor.name, 'src_uuid': s_flavor.id, 'src_cloud': s_cloud_name, 'dst_flavor_name': new_flavor_name, 'dst_uuid': "NULL", 'dst_cloud': t_cloud_name, 'state': "unknown"} # create a new tenant try: migrated_flavor = self.nv_target.flavors.create( **new_flavor_details) flavor_migration_data.update({'dst_uuid': migrated_flavor.id}) except Exception as e: # TODO: not sure what exactly the exception will be thrown # TODO: upon creation failure print "flavor '{}' migration failure\nDetails:"\ .format(s_flavor.name, e.message) # update database record flavor_migration_data.update({'state': "error"}) flavors.record_flavor_migrated([flavor_migration_data]) return flavor_migration_data.update({'state': "completed"}) flavors.record_flavor_migrated([flavor_migration_data])
def migrate_one_flavor(self, flavor_name): try: s_flavor = self.nv_source.flavors.find(name=flavor_name) except nova_exceptions.NotFound: # encapsulate exceptions to make it more understandable # to user. Other exception handling mechanism can be added later LOG.error(exceptions.ResourceNotFoundException( ResourceType.flavor, flavor_name, cfg.CONF.SOURCE.os_cloud_name)) return s_cloud_name = cfg.CONF.SOURCE.os_cloud_name t_cloud_name = cfg.CONF.TARGET.os_cloud_name # check whether the tenant has been migrated values = [flavor_name, s_flavor.id, s_cloud_name, t_cloud_name] m_flavor = flavors.get_migrated_flavor(values) new_flavor_name = flavor_name if m_flavor is not None and m_flavor['state'] == "completed": LOG.info("flavor {0} in cloud {1} has already been migrated" .format(m_flavor["src_flavor_name"], s_cloud_name)) return elif m_flavor is not None and m_flavor['state'] != "completed": LOG.info("Retrying migrating {0} from {1}" .format(flavor_name, s_cloud_name)) elif m_flavor is None: # check for tenant name duplication new_flavor_name = s_flavor.name try: if self.duplicates_handle == "SKIP": found = self.nv_target.flavors.find(name=new_flavor_name) if found: LOG.info("Skipping flavor '{0}' duplicates" "found on cloud '{1}'" .format(found.name, t_cloud_name)) return elif self.duplicates_handle == "AUTO_RENAME": found = True while found: found = self.nv_target.flavors.find(name=new_flavor_name) if found: new_flavor_name += "_migrated" except nova_exceptions.NotFound: # irrelevant exception swallow the exception pass # preparing record for inserting into database flavor_migration_data = {'src_flavor_name': s_flavor.name, 'src_uuid': s_flavor.id, 'src_cloud': s_cloud_name, 'dst_flavor_name': new_flavor_name, 'dst_uuid': "NULL", 'dst_cloud': t_cloud_name, 'state': "unknown"} flavors.record_flavor_migrated([flavor_migration_data]) LOG.info("Start migrating flavour '{}'\n".format(s_flavor.name)) swap = getattr(s_flavor, 'swap', 0) if not swap: swap = 0 else: swap = int(swap) new_flavor_details = { 'name': new_flavor_name, 'ram': s_flavor.ram, 'vcpus': s_flavor.vcpus, 'disk': s_flavor.disk, 'ephemeral': getattr(s_flavor, 'ephemeral', 0), 'swap': swap, 'rxtx_factor': s_flavor.rxtx_factor, 'is_public': getattr(s_flavor, 'os-flavor-access:is_public', False)} # create a new tenant flavour_data = flavors.get_migrated_flavor(values) try: migrated_flavor = self.nv_target.flavors.create( **new_flavor_details) flavour_data.update({'dst_uuid': migrated_flavor.id}) except Exception as e: LOG.error("flavor '{}' migration failure\nDetails:" .format(s_flavor.name, e.args)) # update database record flavour_data.update({'state': "error"}) flavors.update_migration_record(**flavour_data) return flavour_data.update({'state': "completed"}) flavors.update_migration_record(**flavour_data)