class TestDeprovision(TestCase): """ Same as above, but calls deprovision in the test (Test is same as previous) """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT, NETWORK) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) self.good_bmi.provision(NODE_NAME, EXIST_IMG_NAME, NETWORK, NIC) time.sleep(constants.HIL_CALL_TIMEOUT) def runTest(self): response = self.good_bmi.deprovision(NODE_NAME, NETWORK, NIC) self.assertEqual(response[constants.STATUS_CODE_KEY], 200) time.sleep(constants.HIL_CALL_TIMEOUT) def tearDown(self): self.good_bmi.remove_image(EXIST_IMG_NAME) self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown()
class TestCopyImage(TestCase): """ Creating a flatten copy of an image """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) def runTest(self): response = self.good_bmi.copy_image(EXIST_IMG_NAME, PROJECT, IMG2) self.assertEqual(response[constants.STATUS_CODE_KEY], 200) images = self.db.image.fetch_images_from_project(PROJECT) exists_image = False for image in images: if IMG2 == image: exists_image = True break self.assertTrue(exists_image) with ceph.RBD(_cfg.fs, _cfg.iscsi.password) as fs: img_id = self.good_bmi.get_ceph_image_name_from_project( IMG2, PROJECT) fs.get_image(img_id) def tearDown(self): self.good_bmi.remove_image(IMG2) self.good_bmi.remove_image(EXIST_IMG_NAME) self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown()
class TestListImages(TestCase): """ Imports an import image and calls the list images rest call """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) def runTest(self): data = {constants.PROJECT_PARAMETER: PROJECT} res = requests.post(PICASSO_URL + "list_images/", data=data, auth=(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD)) js = res.json() self.assertEqual(res.status_code, 200) self.assertEqual(js[0], EXIST_IMG_NAME) def tearDown(self): self.good_bmi.remove_image(EXIST_IMG_NAME) self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown()
class TestListSnapshots(TestCase): """ Does the same steps as previous test and calls list snapshots rest call """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) self.good_bmi.create_disk(NEW_DISK, EXIST_IMG_NAME) self.good_bmi.create_snapshot(NEW_DISK, NEW_SNAP_NAME) def runTest(self): data = {constants.PROJECT_PARAMETER: PROJECT} res = requests.post(PICASSO_URL + "list_snapshots/", data=data, auth=(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD)) self.assertEqual(res.status_code, 200) js = res.json() self.assertEqual(res.status_code, 200) self.assertEqual(js[0][0], NEW_SNAP_NAME) def tearDown(self): self.good_bmi.delete_disk(NEW_DISK) self.good_bmi.remove_image(NEW_SNAP_NAME) self.good_bmi.remove_image(EXIST_IMG_NAME) self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown() time.sleep(constants.HIL_CALL_TIMEOUT)
class TestRemoveImage(TestCase): """ Imports an Image and calls the remove image rest call """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) def runTest(self): data = { constants.PROJECT_PARAMETER: PROJECT, constants.IMAGE_NAME_PARAMETER: EXIST_IMG_NAME } res = requests.delete(PICASSO_URL + "remove_image/", data=data, auth=(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD)) self.assertEqual(res.status_code, 200) def tearDown(self): self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown()
class TestProvision(TestCase): """ Tests Rest Provision call by importing an image and calling provision """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) self.good_bmi.create_disk(NEW_DISK, EXIST_IMG_NAME) def runTest(self): data = { constants.PROJECT_PARAMETER: PROJECT, constants.NODE_NAME_PARAMETER: NODE_NAME, constants.DISK_NAME_PARAMETER: NEW_DISK, constants.NIC_PARAMETER: NIC } res = requests.put(PICASSO_URL + "provision/", data=data, auth=(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD)) self.assertEqual(res.status_code, 200) time.sleep(constants.HIL_CALL_TIMEOUT) def tearDown(self): self.good_bmi.delete_disk(NEW_DISK) self.good_bmi.deprovision(NODE_NAME, NIC) self.good_bmi.remove_image(EXIST_IMG_NAME) self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown() time.sleep(constants.HIL_CALL_TIMEOUT)
class TestInsert(TestCase): """ Creates a project and tests inserting images """ @trace def setUp(self): self.db = Database() self.db.project.insert('project 1') def runTest(self): self.db.image.insert('image 1', 1) self.db.image.insert('image 2', 1, is_public=True) self.db.image.insert('image 3', 1, parent_id=1) self.db.image.insert('image 4', 1, is_snapshot=True, parent_id=1) images = self.db.image.fetch_images_from_project('project 1') self.assertTrue('image 1' in images and 'image 2' in images) images = self.db.image.fetch_clones_from_project('project 1') self.assertTrue('image 3' in [img[0] for img in images]) snapshots = self.db.image.fetch_snapshots_from_project('project 1') self.assertTrue('image 4' in [snapshot[0] for snapshot in snapshots]) images = self.db.image.fetch_names_with_public() self.assertTrue('image 2' in images) def tearDown(self): self.db.project.delete_with_name('project 1') self.db.close()
class TestListSnapshots(TestCase): """ Creates snapshot like previous and calls list snapshots """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT, NETWORK) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) self.good_bmi.provision(NODE_NAME, EXIST_IMG_NAME, NETWORK, NIC) time.sleep(constants.HIL_CALL_TIMEOUT) self.good_bmi.create_snapshot(NODE_NAME, NEW_SNAP_NAME) def runTest(self): response = self.good_bmi.list_snapshots() self.assertEqual(response[constants.STATUS_CODE_KEY], 200) self.assertEqual(response[constants.RETURN_VALUE_KEY][0][0], NEW_SNAP_NAME) def tearDown(self): self.good_bmi.deprovision(NODE_NAME, NETWORK, NIC) self.good_bmi.remove_image(NEW_SNAP_NAME) self.good_bmi.remove_image(EXIST_IMG_NAME) self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown() time.sleep(constants.HIL_CALL_TIMEOUT)
class TestMove(TestCase): """ Inserts images and tries moving them """ @trace def setUp(self): self.db = Database() self.db.project.insert('project 1') self.db.project.insert('project 2') self.db.image.insert('image 1', 1) self.db.image.insert('image 2', 1, is_snapshot=True, parent_id=1) def runTest(self): self.db.image.move_image('project 1', 'image 1', 2, None) self.db.image.move_image('project 1', 'image 2', 1, 'image 3') images = self.db.image.fetch_images_from_project('project 2') images1 = self.db.image.fetch_images_from_project('project 1') self.assertTrue('image 1' in images and 'image 1' not in images1) snaps = self.db.image.fetch_snapshots_from_project('project 1') self.assertTrue(('image 3' in [s[0] for s in snaps]) and not ('image 2' in [s[0] for s in snaps])) def tearDown(self): self.db.project.delete_with_name('project 1') self.db.project.delete_with_name('project 2') self.db.close()
class TestMove(TestCase): """ Inserts images and tries moving them """ @trace def setUp(self): self.db = Database() self.db.project.insert('project 1', 'network 1') self.db.project.insert('project 2', 'network 2') self.db.image.insert('image 1', 1) self.db.image.insert('image 2', 1, is_snapshot=True, parent_id=1) def runTest(self): self.db.image.move_image('project 1', 'image 1', 2, None) self.db.image.move_image('project 1', 'image 2', 1, 'image 3') images = self.db.image.fetch_images_from_project('project 2') images1 = self.db.image.fetch_images_from_project('project 1') self.assertTrue('image 1' in images and 'image 1' not in images1) snaps = self.db.image.fetch_snapshots_from_project('project 1') self.assertTrue(('image 3' in [s[0] for s in snaps]) and not ('image 2' in [s[0] for s in snaps])) def tearDown(self): self.db.project.delete_with_name('project 1') self.db.project.delete_with_name('project 2') self.db.close()
class TestInsert(TestCase): """ Creates a project and tests inserting images """ @trace def setUp(self): self.db = Database() self.db.project.insert('project 1', 'network 1') def runTest(self): self.db.image.insert('image 1', 1) self.db.image.insert('image 2', 1, is_public=True) self.db.image.insert('image 3', 1, parent_id=1) self.db.image.insert('image 4', 1, is_snapshot=True, parent_id=1) images = self.db.image.fetch_images_from_project('project 1') self.assertTrue('image 1' in images and 'image 2' in images) images = self.db.image.fetch_clones_from_project('project 1') self.assertTrue('image 3' in [img[0] for img in images]) snapshots = self.db.image.fetch_snapshots_from_project('project 1') self.assertTrue('image 4' in [snapshot[0] for snapshot in snapshots]) images = self.db.image.fetch_names_with_public() self.assertTrue('image 2' in images) def tearDown(self): self.db.project.delete_with_name('project 1') self.db.close()
class TestListSnapshots(TestCase): """ Creates snapshot like previous and calls list snapshots """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) self.good_bmi.create_disk(NEW_DISK, EXIST_IMG_NAME) time.sleep(constants.HIL_CALL_TIMEOUT) self.good_bmi.create_snapshot(NEW_DISK, NEW_SNAP_NAME) def runTest(self): response = self.good_bmi.list_snapshots() self.assertEqual(response[constants.STATUS_CODE_KEY], 200) self.assertEqual(response[constants.RETURN_VALUE_KEY][0][0], NEW_SNAP_NAME) def tearDown(self): self.good_bmi.delete_disk(NEW_DISK) self.good_bmi.remove_image(NEW_SNAP_NAME) self.good_bmi.remove_image(EXIST_IMG_NAME) self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown() time.sleep(constants.HIL_CALL_TIMEOUT)
class TestDelete(TestCase): """ Inserts and Deletes Project """ @trace def setUp(self): self.db = Database() self.db.project.insert('project 1', 'network 1') def runTest(self): self.db.project.delete_with_name('project 1') projects = self.db.project.fetch_projects() self.assertFalse('project 1' in projects) def tearDown(self): self.db.close()
class TestFetch(TestCase): """ Inserts several images and tests all fetch calls """ @trace def setUp(self): self.db = Database() self.db.project.insert('project 1', 'network 1') self.db.image.insert('image 1', 1) self.db.image.insert('image 2', 1, is_public=True) self.db.image.insert('image 3', 1, parent_id=1) self.db.image.insert('image 4', 1, is_snapshot=True, parent_id=1) def test_image_fetch(self): images = self.db.image.fetch_images_from_project('project 1') self.assertTrue('image 1' in images and 'image 2' in images) images = self.db.image.fetch_clones_from_project('project 1') self.assertTrue('image 3' in [img[0] for img in images]) snapshots = self.db.image.fetch_snapshots_from_project('project 1') self.assertTrue('image 4' in [snapshot[0] for snapshot in snapshots]) images = self.db.image.fetch_names_with_public() self.assertTrue('image 2' in images) name = self.db.image.fetch_name_with_id(1) self.assertEqual(name, 'image 1') img_id = self.db.image.fetch_id_with_name_from_project('image 2', 'project 1') self.assertEqual(img_id, 2) images = self.db.image.fetch_all_images() self.assertEqual(images.__len__(), 4) self.assertEqual([image[1] for image in images], ['image 1', 'image 2', 'image 3', 'image 4']) def test_nonexistent_image_fetch(self): """ Tries to retrieve the image id of an image that doesn't exist in the db. Should raise an error """ with self.assertRaises(db_exceptions.ImageNotFoundException): img_id = self.db.image.fetch_id_with_name_from_project( 'nonexistent_image', 'project 1') def tearDown(self): self.db.project.delete_with_name('project 1') self.db.close()
class TestFetch(TestCase): """ Inserts several images and tests all fetch calls """ @trace def setUp(self): self.db = Database() self.db.project.insert('project 1') self.db.image.insert('image 1', 1) self.db.image.insert('image 2', 1, is_public=True) self.db.image.insert('image 3', 1, parent_id=1) self.db.image.insert('image 4', 1, is_snapshot=True, parent_id=1) def test_image_fetch(self): images = self.db.image.fetch_images_from_project('project 1') self.assertTrue('image 1' in images and 'image 2' in images) images = self.db.image.fetch_clones_from_project('project 1') self.assertTrue('image 3' in [img[0] for img in images]) snapshots = self.db.image.fetch_snapshots_from_project('project 1') self.assertTrue('image 4' in [snapshot[0] for snapshot in snapshots]) images = self.db.image.fetch_names_with_public() self.assertTrue('image 2' in images) name = self.db.image.fetch_name_with_id(1) self.assertEqual(name, 'image 1') img_id = self.db.image.fetch_id_with_name_from_project( 'image 2', 'project 1') self.assertEqual(img_id, 2) images = self.db.image.fetch_all_images() self.assertEqual(images.__len__(), 4) self.assertEqual([image[1] for image in images], ['image 1', 'image 2', 'image 3', 'image 4']) def test_nonexistent_image_fetch(self): """ Tries to retrieve the image id of an image that doesn't exist in the db. Should raise an error """ with self.assertRaises(db_exceptions.ImageNotFoundException): img_id = self.db.image.fetch_id_with_name_from_project( 'nonexistent_image', 'project 1') def tearDown(self): self.db.project.delete_with_name('project 1') self.db.close()
class TestDelete(TestCase): """ Inserts image and deletes it """ @trace def setUp(self): self.db = Database() self.db.project.insert('project 1') self.db.image.insert('image 1', 1) def runTest(self): self.db.image.delete_with_name_from_project('image 1', 'project 1') images = self.db.image.fetch_images_from_project('project 1') self.assertFalse('image 1' in images) def tearDown(self): self.db.project.delete_with_name('project 1') self.db.close()
class TestInsert(TestCase): """ Inserts Project """ @trace def setUp(self): self.db = Database() def runTest(self): self.db.project.insert('project 1', 'network 1') projects = self.db.project.fetch_projects() self.assertEqual('project 1', projects[0][1]) def tearDown(self): self.db.project.delete_with_name('project 1') self.db.close()
class TestDelete(TestCase): """ Inserts image and deletes it """ @trace def setUp(self): self.db = Database() self.db.project.insert('project 1', 'network 1') self.db.image.insert('image 1', 1) def runTest(self): self.db.image.delete_with_name_from_project('image 1', 'project 1') images = self.db.image.fetch_images_from_project('project 1') self.assertFalse('image 1' in images) def tearDown(self): self.db.project.delete_with_name('project 1') self.db.close()
class TestFetch(TestCase): """ Inserts and Fetches Projects """ @trace def setUp(self): self.db = Database() self.db.project.insert('project 1', 'network 1') def runTest(self): projects = self.db.project.fetch_projects() self.assertTrue('project 1' in projects[0]) pid = self.db.project.fetch_id_with_name('project 1') self.assertEqual(pid, 1) def tearDown(self): self.db.project.delete_with_name('project 1') self.db.close()
class TestCreateSnapshot(TestCase): """ Calls provision like TestProvision then creates a snapshot using rest call """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) self.good_bmi.create_disk(NEW_DISK, EXIST_IMG_NAME) time.sleep(constants.HIL_CALL_TIMEOUT) def runTest(self): data = { constants.PROJECT_PARAMETER: PROJECT, constants.DISK_NAME_PARAMETER: NEW_DISK, constants.SNAP_NAME_PARAMETER: NEW_SNAP_NAME } res = requests.put(PICASSO_URL + "create_snapshot/", data=data, auth=(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD)) self.assertEqual(res.status_code, 200) snaps = self.db.image.fetch_snapshots_from_project(PROJECT) has_image = False for snapshot in snaps: if NEW_SNAP_NAME == snapshot[0]: has_image = True self.assertTrue(has_image) with ceph.RBD(_cfg.fs, _cfg.iscsi.password) as fs: img_id = self.good_bmi.get_ceph_image_name_from_project( NEW_SNAP_NAME, PROJECT) fs.get_image(img_id) def tearDown(self): self.good_bmi.delete_disk(NEW_DISK) self.good_bmi.remove_image(NEW_SNAP_NAME) self.good_bmi.remove_image(EXIST_IMG_NAME) self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown() time.sleep(constants.HIL_CALL_TIMEOUT)
class TestRemoveImage(TestCase): """ Imports an image and calls remove image """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT, NETWORK) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) def runTest(self): response = self.good_bmi.remove_image(EXIST_IMG_NAME) self.assertEqual(response[constants.STATUS_CODE_KEY], 200) def tearDown(self): self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown()
class TestRemoveImage(TestCase): """ Imports an image and calls remove image """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) def runTest(self): response = self.good_bmi.remove_image(EXIST_IMG_NAME) self.assertEqual(response[constants.STATUS_CODE_KEY], 200) def tearDown(self): self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown()
class TestCreateSnapshot(TestCase): """ Provisions an imported image and creates snapshot """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT, NETWORK) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) self.good_bmi.provision(NODE_NAME, EXIST_IMG_NAME, NETWORK, NIC) time.sleep(constants.HIL_CALL_TIMEOUT) def runTest(self): response = self.good_bmi.create_snapshot(NODE_NAME, NEW_SNAP_NAME) self.assertEqual(response[constants.STATUS_CODE_KEY], 200) snaps = self.db.image.fetch_snapshots_from_project(PROJECT) has_image = False for snapshot in snaps: if NEW_SNAP_NAME == snapshot[0]: has_image = True self.assertTrue(has_image) with ceph.RBD(_cfg.fs, _cfg.iscsi.password) as fs: img_id = self.good_bmi.get_ceph_image_name_from_project( NEW_SNAP_NAME, PROJECT) fs.get_image(img_id) def tearDown(self): self.good_bmi.deprovision(NODE_NAME, NETWORK, NIC) self.good_bmi.remove_image(NEW_SNAP_NAME) self.good_bmi.remove_image(EXIST_IMG_NAME) self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown() time.sleep(constants.HIL_CALL_TIMEOUT)
class TestProvisionDeprovision(TestCase): """ This tests multiple things. It creates an image, then provisions from it and then deprovisions the node and delete the image. """ @trace def setUp(self): self.db = Database() self.db.project.insert(PROJECT) self.good_bmi = BMI(CORRECT_HIL_USERNAME, CORRECT_HIL_PASSWORD, PROJECT) self.good_bmi.import_ceph_image(EXIST_IMG_NAME) def runTest(self): # First create a disk response = self.good_bmi.create_disk(NEW_DISK, EXIST_IMG_NAME) self.assertEqual(response[constants.STATUS_CODE_KEY], 200) # Then provision a node from that disk response = self.good_bmi.provision(NODE_NAME, NEW_DISK, NIC) self.assertEqual(response[constants.STATUS_CODE_KEY], 200) # Then deprovision that node response = self.good_bmi.deprovision(NODE_NAME, NIC) self.assertEqual(response[constants.STATUS_CODE_KEY], 200) # Delete the disk response = self.good_bmi.delete_disk(NEW_DISK) self.assertEqual(response[constants.STATUS_CODE_KEY], 200) def tearDown(self): self.good_bmi.remove_image(EXIST_IMG_NAME) self.db.project.delete_with_name(PROJECT) self.db.close() self.good_bmi.shutdown() time.sleep(constants.HIL_CALL_TIMEOUT)
class BMI: @log def __init__(self, *args): if args.__len__() == 1: credentials = args[0] self.cfg = config.get() self.db = Database() self.__process_credentials(credentials) self.hil = HIL(base_url=self.cfg.net_isolator.url, usr=self.username, passwd=self.password) self.fs = RBD(self.cfg.fs, self.cfg.iscsi.password) self.dhcp = DNSMasq() # self.iscsi = IET(self.fs, self.config.iscsi_update_password) # Need to make this generic by passing specific config self.iscsi = TGT(self.cfg.fs.conf_file, self.cfg.fs.id, self.cfg.fs.pool) elif args.__len__() == 3: username, password, project = args self.cfg = config.get() self.username = username self.password = password self.proj = project self.db = Database() self.pid = self.__does_project_exist(self.proj) self.is_admin = self.__check_admin() self.hil = HIL(base_url=self.cfg.net_isolator.url, usr=self.username, passwd=self.password) self.fs = RBD(self.cfg.fs, self.cfg.iscsi.password) logger.debug("Username is %s and Password is %s", self.username, self.password) self.dhcp = DNSMasq() # self.iscsi = IET(self.fs, self.config.iscsi_update_password) self.iscsi = TGT(self.cfg.fs.conf_file, self.cfg.fs.id, self.cfg.fs.pool) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.shutdown() @trace def __does_project_exist(self, project): pid = self.db.project.fetch_id_with_name(project) # None as a query result implies that the project does not exist. if pid is None: logger.info("Raising Project Not Found Exception for %s", project) raise db_exceptions.ProjectNotFoundException(project) return pid # this method will determine whether user is admin (still unclear on doing # it properly) def __check_admin(self): return True @trace def __get_ceph_image_name(self, name): img_id = self.db.image.fetch_id_with_name_from_project(name, self.proj) return str(self.cfg.bmi.uid) + "img" + str(img_id) def get_ceph_image_name_from_project(self, name, project_name): img_id = self.db.image.fetch_id_with_name_from_project( name, project_name) if img_id is None: logger.info("Raising Image Not Found Exception for %s", name) raise db_exceptions.ImageNotFoundException(name) return str(self.cfg.bmi.uid) + "img" + str(img_id) @trace def __extract_id(self, ceph_img_name): start_index = ceph_img_name.find("img") start_index += 3 img_id = ceph_img_name[start_index:] return img_id @trace def __process_credentials(self, credentials): base64_str, self.proj = credentials self.pid = self.__does_project_exist(self.proj) self.username, self.password = tuple( base64.b64decode(base64_str).split(':')) logger.debug("Username is %s and Password is %s", self.username, self.password) self.is_admin = self.__check_admin() @log def __register(self, node_name, img_name, target_name, mac_addr): logger.debug("The Mac Addr File name is %s", mac_addr) self.__generate_ipxe_file(node_name, target_name) self.__generate_mac_addr_file(img_name, node_name, mac_addr) @log def __unregister(self, node_name, mac_addr): logger.debug("The Mac Addr File name is %s", mac_addr) self.__delete_ipxe_file(node_name) self.__delete_mac_addr_file(mac_addr) @log def __delete_ipxe_file(self, node_name): """Delete the ipxe file""" ipxe_file = self.cfg.tftp.ipxe_path + node_name + ".ipxe" self.__delete_file(ipxe_file) @log def __delete_mac_addr_file(self, mac_addr): """Delete the mac_addr file""" mac_addr_file = self.cfg.tftp.pxelinux_path + mac_addr self.__delete_file(mac_addr_file) @log def __delete_file(self, file_path): """Helper function to delete a file. If the file does not exist, consider it a success. """ # FIXME: This method doesn't need to be on the class at all. # Moving it out of the class breaks logging for this function, # which is weird. try: os.remove(file_path) except OSError as e: if e.errno == errno.ENOENT: logger.info("mac_addr file does not exist") return raise RegistrationFailedException(node_name, e.strerror) @log def __generate_ipxe_file(self, node_name, target_name): template_loc = os.path.abspath( os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) logger.debug("Template LOC = %s", template_loc) path = self.cfg.tftp.ipxe_path + node_name + ".ipxe" logger.debug("The Path for ipxe file is %s", path) try: with open(path, 'w') as ipxe: for line in open(template_loc + "/ipxe.temp", 'r'): line = line.replace(constants.IPXE_TARGET_NAME, target_name) line = line.replace(constants.IPXE_ISCSI_IP, self.cfg.iscsi.ip) ipxe.write(line) logger.info("Generated ipxe file") os.chmod(path, 0755) logger.info("Changed permissions to 755") except (OSError, IOError) as e: logger.info("Raising Registration Failed Exception for %s", node_name) raise RegistrationFailedException(node_name, e.message) @log def __generate_mac_addr_file(self, img_name, node_name, mac_addr): template_loc = os.path.abspath( os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) logger.debug("Template LOC = %s", template_loc) path = self.cfg.tftp.pxelinux_path + mac_addr logger.debug("The Path for mac addr file is %s", path) try: with open(path, 'w') as mac: for line in open(template_loc + "/mac.temp", 'r'): line = line.replace(constants.MAC_IMG_NAME, img_name) line = line.replace(constants.MAC_IPXE_NAME, node_name + ".ipxe") mac.write(line) logger.info("Generated mac addr file") os.chmod(path, 0644) logger.debug("Changed permissions to 644") except (OSError, IOError) as e: logger.info("Raising Registration Failed Exception for %s", node_name) raise RegistrationFailedException(node_name, e.message) # Parses the Exception and returns the dict that should be returned to user @log def __return_error(self, ex): # Replaces the image name with id in error string @log def swap_id_with_name(err_str): parts = err_str.split(" ") start_index = parts[0].find("img") if start_index != -1: start_index += 3 img_id = parts[0][start_index:] name = self.db.image.fetch_name_with_id(img_id) if name is not None: parts[0] = name return " ".join(parts) logger.debug("Checking if FileSystemException") if FileSystemException in ex.__class__.__bases__: logger.debug("It is FileSystemException") return { constants.STATUS_CODE_KEY: ex.status_code, constants.MESSAGE_KEY: swap_id_with_name(str(ex)) } return { constants.STATUS_CODE_KEY: ex.status_code, constants.MESSAGE_KEY: str(ex) } # A custom function which is wrapper around only success code that # we are creating. @log def __return_success(self, obj): return { constants.STATUS_CODE_KEY: 200, constants.RETURN_VALUE_KEY: obj } @log def shutdown(self): self.fs.tear_down() self.db.close() # Provisions from HIL and Boots the given node with given image @log def create_disk(self, disk_name, img_name): """ Creates a new image named <disk_name> from <img_name>, and then creates an iscsi endpoint for <disk_name>. Returns the disk image name (which also happens to be the iscsi target name) Note: The name of the iscsi target and the name of disk in ceph is same """ # Database Operations try: # Find the image id by using the image name, and then create a new # entry for the new disk image. parent_id = self.db.image.fetch_id_with_name_from_project( img_name, self.proj) self.db.image.insert(disk_name, self.pid, parent_id) # This generates the name that BMI *must* have used when it created # the copy in ceph of img_name ceph_img_name = self.__get_ceph_image_name(img_name) # Prepare the ceph image name to be used. clone_ceph_name = self.__get_ceph_image_name(disk_name) except db_exceptions.ORMException as e: clone_ceph_name = self.__get_ceph_image_name(disk_name) return { constants.STATUS_CODE_KEY: 409, constants.MESSAGE_KEY: "Disk exists. Endpoint:" + clone_ceph_name } except DBException as e: logger.exception('') return self.__return_error(e) # Storage Operations try: self.fs.clone(ceph_img_name, self.cfg.bmi.snapshot, clone_ceph_name) except FileSystemException as e: logger.exception('') self.db.image.delete_with_name_from_project(disk_name, self.proj) # iSCSI Operations try: self.iscsi.add_target(clone_ceph_name) except ISCSIException as e: logger.exception('') self.fs.remove(clone_ceph_name) self.db.image.delete_with_name_from_project(disk_name, self.proj) logger.info("The create_disk command was executed successfully") # This will be changed to return a list of all targets, with each # target including the endpoint, port and target name. return self.__return_success(clone_ceph_name) @log def delete_disk(self, disk_name): """ Deletes the disk image <disk_name>. Also deletes the iSCSI target pointing to that image. """ try: # Get the ceph image name. ceph_img_name = self.__get_ceph_image_name(disk_name) except DBException as e: logger.exception('') return self.__return_error(e) try: self.iscsi.remove_target(ceph_img_name) except ISCSIException as e: logger.exception('') return self.__return_error(e) try: ret = self.fs.remove(str(ceph_img_name).encode("utf-8")) except FileSystemException as e: # what if this fails? Also, we should check if it doesnt exists # in ceph then continue, otherwise raise an error. self.isci.add_target(ceph_img_name) return self.__return_error(e) # If __get_ceph_image_name(disk_name) passes, then it confirms that # existence of disk_name, and then this delete from db command should # succeed. Will think more if I need to wrap this up in a try except # block. self.db.image.delete_with_name_from_project(disk_name, self.proj) return self.__return_success(ret) @log def provision(self, node_name, disk_name, nic): """ This takes in the name of the disk, and then creates the ipxe and macaddress file for <node> with <nic> """ try: mac_addr = "01-" + self.hil.get_node_mac_addr(node_name, nic). \ replace(":", "-") clone_ceph_name = self.__get_ceph_image_name(disk_name) self.__register(node_name, disk_name, clone_ceph_name, mac_addr) return self.__return_success(True) except (RegistrationFailedException, DBException, HILException) as e: # Message is being handled by custom formatter logger.exception('') return self.__return_error(e) @log def deprovision(self, node_name, nic): """ This takes in the name of the node and nic to deprovision. This method deletes the ipxe file and the bootfile. """ try: mac_addr = "01-" + self.hil.get_node_mac_addr(node_name, nic). \ replace(":", "-") self.__unregister(node_name, mac_addr) return self.__return_success(True) except (RegistrationFailedException, HILException) as e: # Message is being handled by custom formatter logger.exception('') return self.__return_error(e) # Creates snapshot for the given image with snap_name as given name # fs_obj will be populated by decorator @log def create_snapshot(self, disk_name, snap_name): try: self.hil.validate_project(self.proj) ceph_img_name = self.__get_ceph_image_name(disk_name) self.fs.snap_image(ceph_img_name, self.cfg.bmi.snapshot) self.fs.snap_protect(ceph_img_name, self.cfg.bmi.snapshot) parent_id = self.db.image.fetch_parent_id(self.proj, disk_name) self.db.image.insert(snap_name, self.pid, parent_id, is_snapshot=True) snap_ceph_name = self.__get_ceph_image_name(snap_name) self.fs.clone(ceph_img_name, self.cfg.bmi.snapshot, snap_ceph_name) self.fs.flatten(snap_ceph_name) self.fs.snap_image(snap_ceph_name, self.cfg.bmi.snapshot) self.fs.snap_protect(snap_ceph_name, self.cfg.bmi.snapshot) self.fs.snap_unprotect(ceph_img_name, self.cfg.bmi.snapshot) self.fs.remove_snapshot(ceph_img_name, self.cfg.bmi.snapshot) return self.__return_success(True) except (HILException, DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) # Lists snapshot for the given image img_name # URL's have to be read from BMI config file # fs_obj will be populated by decorator @log def list_snapshots(self): try: self.hil.validate_project(self.proj) snapshots = self.db.image.fetch_snapshots_from_project(self.proj) return self.__return_success(snapshots) except (HILException, DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) # Removes snapshot snap_name for the given image img_name # fs_obj will be populated by decorator @log def remove_image(self, img_name): try: self.hil.validate_project(self.proj) ceph_img_name = self.__get_ceph_image_name(img_name) self.fs.snap_unprotect(ceph_img_name, self.cfg.bmi.snapshot) self.fs.remove_snapshot(ceph_img_name, self.cfg.bmi.snapshot) self.fs.remove(ceph_img_name) self.db.image.delete_with_name_from_project(img_name, self.proj) return self.__return_success(True) except (HILException, DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) # Lists the images for the project which includes the snapshot @log def list_images(self): try: self.hil.validate_project(self.proj) names = self.db.image.fetch_images_from_project(self.proj) return self.__return_success(names) except (HILException, DBException) as e: logger.exception('') return self.__return_error(e) @log def list_disks(self): """Show all disks that belong to <project>.""" try: clones = self.db.image.fetch_clones_from_project(self.proj) return self.__return_success(clones) except DBException as e: logger.exception('') return self.__return_error(e) @log def list_all_images(self): try: images = self.db.image.fetch_all_images() new_images = [] for image in images: image.insert( 3, self.get_ceph_image_name_from_project(image[1], image[2])) new_images.append(image) return self.__return_success(new_images) except DBException as e: logger.exception('') return self.__return_error(e) @log def import_ceph_image(self, img): """ Import an image from ceph to be used by BMI Clone an image in ceph to be used by BMI. : param img: Name of image in ceph : return: True on successful completion """ try: ceph_img_name = str(img) # create a snapshot of the golden image and protect it # this is needed because, in ceph, you can only create clones from # snapshots. self.fs.snap_image(ceph_img_name, self.cfg.bmi.snapshot) self.fs.snap_protect(ceph_img_name, self.cfg.bmi.snapshot) # insert golden image name into bmi db self.db.image.insert(ceph_img_name, self.pid) # get a name for our copy of the golden image. For instance an # image in ceph called centos6.7, after cloning, will be a given # a name like 4img1 based on the UID in config and image id in db snap_ceph_name = self.__get_ceph_image_name(ceph_img_name) # clone the snapshot of the golden image and then flatten it self.fs.clone(ceph_img_name, self.cfg.bmi.snapshot, snap_ceph_name) self.fs.flatten(snap_ceph_name) # create a snapshot of our newly created golden image so that when # we provision, we can easily make clones from this readily # available snapshot. self.fs.snap_image(snap_ceph_name, self.cfg.bmi.snapshot) self.fs.snap_protect(snap_ceph_name, self.cfg.bmi.snapshot) # unprotect and delete the snapshot of the original golden because # we no longer need it. self.fs.snap_unprotect(ceph_img_name, self.cfg.bmi.snapshot) self.fs.remove_snapshot(ceph_img_name, self.cfg.bmi.snapshot) return self.__return_success(True) except (DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) @log def import_ceph_snapshot(self, img, snap_name, protect): """ Import a snapshot from ceph to be used by BMI Clone a snapshot in ceph to be used by BMI. Similar to import_ceph_image except we can directly start the cloning process because it is already a snapshot. : param img: Name of snapshot in ceph : return: True on successful completion """ try: ceph_img_name = str(img) if protect: self.fs.snap_protect(ceph_img_name, snap_name) self.db.image.insert(ceph_img_name, self.pid) snap_ceph_name = self.__get_ceph_image_name(ceph_img_name) self.fs.clone(ceph_img_name, snap_name, snap_ceph_name) self.fs.flatten(snap_ceph_name) self.fs.snap_image(snap_ceph_name, self.cfg.bmi.snapshot) self.fs.snap_protect(snap_ceph_name, self.cfg.bmi.snapshot) return self.__return_success(True) except (DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) @log def export_ceph_image(self, img, name): try: ceph_img_name = self.__get_ceph_image_name(img) self.fs.clone(ceph_img_name, self.cfg.bmi.snapshot, name) self.fs.flatten(name) return self.__return_success(True) except FileSystemException as e: logger.exception('') return self.__return_error(e) @log def delete_image(self, project, img): try: if not self.is_admin: raise AuthorizationFailedException() self.db.image.delete_with_name_from_project(img, project) return self.__return_success(True) except (DBException, AuthorizationFailedException) as e: logger.exception('') return self.__return_error(e) @log def add_image(self, project, img, id, snap, parent, public): try: if not self.is_admin: raise AuthorizationFailedException() parent_id = None if parent is not None: parent_id = self.db.image.fetch_id_with_name_from_project( parent, project) pid = self.__does_project_exist(project) self.db.image.insert(img, pid, parent_id, public, snap, id) return self.__return_success(True) except (DBException, AuthorizationFailedException) as e: logger.exception('') return self.__return_error(e) @log def get_node_ip(self, node_name): try: mac_addr = self.hil.get_node_mac_addr(node_name) return self.__return_success(self.dhcp.get_ip(mac_addr)) except (HILException, DHCPException) as e: logger.exception('') return self.__return_error(e) @log def copy_image(self, img1, dest_project, img2=None): """ Create a deep copy of src image : param img1: Name of src image : param dest_project: Name of the project where des image will be created : param img2: Name of des image : return: True on successful completion """ try: if not self.is_admin and (self.proj != dest_project): raise AuthorizationFailedException() dest_pid = self.__does_project_exist(dest_project) self.db.image.copy_image(self.proj, img1, dest_pid, img2) if img2 is not None: ceph_name = self.get_ceph_image_name_from_project( img2, dest_project) else: ceph_name = self.get_ceph_image_name_from_project( img1, dest_project) self.fs.clone( self.get_ceph_image_name_from_project(img1, self.proj), self.cfg.bmi.snapshot, ceph_name) self.fs.flatten(ceph_name) self.fs.snap_image(ceph_name, self.cfg.bmi.snapshot) self.fs.snap_protect(ceph_name, self.cfg.bmi.snapshot) return self.__return_success(True) except (DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) @log def move_image(self, img1, dest_project, img2): try: if not self.is_admin and (self.proj != dest_project): raise AuthorizationFailedException() dest_pid = self.__does_project_exist(dest_project) self.db.image.move_image(self.proj, img1, dest_pid, img2) return self.__return_success(True) except DBException as e: logger.exception('') return self.__return_error(e) @log def add_project(self, project, id): try: if not self.is_admin: raise AuthorizationFailedException() self.db.project.insert(project, id) return self.__return_success(True) except (DBException, AuthorizationFailedException) as e: logger.exception('') return self.__return_error(e) @log def delete_project(self, project): try: if not self.is_admin: raise AuthorizationFailedException() self.db.project.delete_with_name(project) return self.__return_success(True) except (DBException, AuthorizationFailedException) as e: logger.exception('') return self.__return_error(e) @log def list_projects(self): try: if not self.is_admin: raise AuthorizationFailedException() projects = self.db.project.fetch_projects() return self.__return_success(projects) except (DBException, AuthorizationFailedException) as e: logger.exception('') return self.__return_error(e) @log def mount_image(self, img): try: if not self.is_admin: raise AuthorizationFailedException() ceph_img_name = self.__get_ceph_image_name(img) self.iscsi.add_target(ceph_img_name) return self.__return_success(True) except (ISCSIException, DBException) as e: logger.exception('') return self.__return_error(e) @log def umount_image(self, img): try: if not self.is_admin: raise AuthorizationFailedException() ceph_img_name = self.__get_ceph_image_name(img) self.iscsi.remove_target(ceph_img_name) return self.__return_success(True) except (ISCSIException, DBException) as e: logger.exception('') return self.__return_error(e) @log def show_mounted(self): try: if not self.is_admin: raise AuthorizationFailedException() mappings = self.iscsi.list_targets() swapped_mappings = {} for k, v in mappings.iteritems(): img_id = self.__extract_id(k) if self.proj == self.db.image.fetch_project_with_id(img_id): swapped_mappings[self.db.image.fetch_name_with_id( img_id)] = v return self.__return_success(swapped_mappings) except (ISCSIException, DBException) as e: logger.exception('') return self.__return_error(e) @log def remake_mappings(self): try: self.iscsi.persist_targets() except (FileSystemException, ISCSIException) as e: logger.exception('') except NotImplementedError: pass
class BMI: @log def __init__(self, *args): if args.__len__() == 1: credentials = args[0] self.cfg = config.get() self.db = Database() self.__process_credentials(credentials) self.hil = HIL(base_url=self.cfg.net_isolator.url, usr=self.username, passwd=self.password) self.fs = RBD(self.cfg.fs, self.cfg.iscsi.password) self.dhcp = DNSMasq() # self.iscsi = IET(self.fs, self.config.iscsi_update_password) # Need to make this generic by passing specific config self.iscsi = TGT(self.cfg.fs.conf_file, self.cfg.fs.id, self.cfg.fs.pool) elif args.__len__() == 3: username, password, project = args self.cfg = config.get() self.username = username self.password = password self.proj = project self.db = Database() self.pid = self.__does_project_exist(self.proj) self.is_admin = self.__check_admin() self.hil = HIL(base_url=self.cfg.net_isolator.url, usr=self.username, passwd=self.password) self.fs = RBD(self.cfg.fs, self.cfg.iscsi.password) logger.debug("Username is %s and Password is %s", self.username, self.password) self.dhcp = DNSMasq() # self.iscsi = IET(self.fs, self.config.iscsi_update_password) self.iscsi = TGT(self.cfg.fs.conf_file, self.cfg.fs.id, self.cfg.fs.pool) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.shutdown() @trace def __does_project_exist(self, project): pid = self.db.project.fetch_id_with_name(project) # None as a query result implies that the project does not exist. if pid is None: logger.info("Raising Project Not Found Exception for %s", project) raise db_exceptions.ProjectNotFoundException(project) return pid # this method will determine whether user is admin (still unclear on doing # it properly) def __check_admin(self): return True @trace def __get_ceph_image_name(self, name): img_id = self.db.image.fetch_id_with_name_from_project(name, self.proj) if img_id is None: logger.info("Raising Image Not Found Exception for %s", name) raise db_exceptions.ImageNotFoundException(name) return str(self.cfg.bmi.uid) + "img" + str(img_id) def get_ceph_image_name_from_project(self, name, project_name): img_id = self.db.image.fetch_id_with_name_from_project( name, project_name) if img_id is None: logger.info("Raising Image Not Found Exception for %s", name) raise db_exceptions.ImageNotFoundException(name) return str(self.cfg.bmi.uid) + "img" + str(img_id) @trace def __extract_id(self, ceph_img_name): start_index = ceph_img_name.find("img") start_index += 3 img_id = ceph_img_name[start_index:] return img_id @trace def __process_credentials(self, credentials): base64_str, self.proj = credentials self.pid = self.__does_project_exist(self.proj) self.username, self.password = tuple( base64.b64decode(base64_str).split(':')) logger.debug("Username is %s and Password is %s", self.username, self.password) self.is_admin = self.__check_admin() @log def __register(self, node_name, img_name, target_name, mac_addr): logger.debug("The Mac Addr File name is %s", mac_addr) self.__generate_ipxe_file(node_name, target_name) self.__generate_mac_addr_file(img_name, node_name, mac_addr) @log def __generate_ipxe_file(self, node_name, target_name): template_loc = os.path.abspath( os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) logger.debug("Template LOC = %s", template_loc) path = self.cfg.tftp.ipxe_path + node_name + ".ipxe" logger.debug("The Path for ipxe file is %s", path) try: with open(path, 'w') as ipxe: for line in open(template_loc + "/ipxe.temp", 'r'): line = line.replace(constants.IPXE_TARGET_NAME, target_name) line = line.replace(constants.IPXE_ISCSI_IP, self.cfg.iscsi.ip) ipxe.write(line) logger.info("Generated ipxe file") os.chmod(path, 0755) logger.info("Changed permissions to 755") except (OSError, IOError) as e: logger.info("Raising Registration Failed Exception for %s", node_name) raise RegistrationFailedException(node_name, e.message) @log def __generate_mac_addr_file(self, img_name, node_name, mac_addr): template_loc = os.path.abspath( os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) logger.debug("Template LOC = %s", template_loc) path = self.cfg.tftp.pxelinux_path + mac_addr logger.debug("The Path for mac addr file is %s", path) try: with open(path, 'w') as mac: for line in open(template_loc + "/mac.temp", 'r'): line = line.replace(constants.MAC_IMG_NAME, img_name) line = line.replace(constants.MAC_IPXE_NAME, node_name + ".ipxe") mac.write(line) logger.info("Generated mac addr file") os.chmod(path, 0644) logger.debug("Changed permissions to 644") except (OSError, IOError) as e: logger.info("Raising Registration Failed Exception for %s", node_name) raise RegistrationFailedException(node_name, e.message) # Parses the Exception and returns the dict that should be returned to user @log def __return_error(self, ex): # Replaces the image name with id in error string @log def swap_id_with_name(err_str): parts = err_str.split(" ") start_index = parts[0].find("img") if start_index != -1: start_index += 3 img_id = parts[0][start_index:] name = self.db.image.fetch_name_with_id(img_id) if name is not None: parts[0] = name return " ".join(parts) logger.debug("Checking if FileSystemException") if FileSystemException in ex.__class__.__bases__: logger.debug("It is FileSystemException") return { constants.STATUS_CODE_KEY: ex.status_code, constants.MESSAGE_KEY: swap_id_with_name(str(ex)) } return { constants.STATUS_CODE_KEY: ex.status_code, constants.MESSAGE_KEY: str(ex) } # A custom function which is wrapper around only success code that # we are creating. @log def __return_success(self, obj): return { constants.STATUS_CODE_KEY: 200, constants.RETURN_VALUE_KEY: obj } @log def shutdown(self): self.fs.tear_down() self.db.close() # Provisions from HIL and Boots the given node with given image @log def provision(self, node_name, img_name, network, nic): try: mac_addr = "01-" + self.hil.get_node_mac_addr(node_name). \ replace(":", "-") self.hil.attach_node_to_project_network(node_name, network, nic) parent_id = self.db.image.fetch_id_with_name_from_project( img_name, self.proj) self.db.image.insert(node_name, self.pid, parent_id) clone_ceph_name = self.__get_ceph_image_name(node_name) ceph_img_name = self.__get_ceph_image_name(img_name) self.fs.clone(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME, clone_ceph_name) ceph_config = self.cfg.fs logger.debug("Contents of ceph_config = %s", str(ceph_config)) self.iscsi.add_target(clone_ceph_name) logger.info("The create command was executed successfully") self.__register(node_name, img_name, clone_ceph_name, mac_addr) return self.__return_success(True) except RegistrationFailedException as e: # Message is being handled by custom formatter # TODO: add a deployment and a unit test for this case. logger.exception('') clone_ceph_name = self.__get_ceph_image_name(node_name) self.iscsi.remove_target(clone_ceph_name) self.fs.remove(clone_ceph_name) self.db.image.delete_with_name_from_project(node_name, self.proj) time.sleep(constants.HIL_CALL_TIMEOUT) self.hil.detach_node_from_project_network(node_name, network, nic) return self.__return_error(e) except ISCSIException as e: # Message is being handled by custom formatter logger.exception('') clone_ceph_name = self.__get_ceph_image_name(node_name) self.fs.remove(clone_ceph_name) self.db.image.delete_with_name_from_project(node_name, self.proj) time.sleep(constants.HIL_CALL_TIMEOUT) self.hil.detach_node_from_project_network(node_name, network, nic) return self.__return_error(e) except FileSystemException as e: # Message is being handled by custom formatter logger.exception('') self.db.image.delete_with_name_from_project(node_name, self.proj) time.sleep(constants.HIL_CALL_TIMEOUT) self.hil.detach_node_from_project_network(node_name, network, nic) return self.__return_error(e) except DBException as e: # Message is being handled by custom formatter logger.exception('') time.sleep(constants.HIL_CALL_TIMEOUT) self.hil.detach_node_from_project_network(node_name, network, nic) return self.__return_error(e) except HILException as e: # Message is being handled by custom formatter logger.exception('') return self.__return_error(e) # This is for detach a node and removing it from iscsi # and destroying its image @log def deprovision(self, node_name, network, nic): ceph_img_name = None try: self.hil.detach_node_from_project_network(node_name, network, nic) ceph_img_name = self.__get_ceph_image_name(node_name) self.db.image.delete_with_name_from_project(node_name, self.proj) ceph_config = self.cfg.fs logger.debug("Contents of ceph+config = %s", str(ceph_config)) self.iscsi.remove_target(ceph_img_name) logger.info("The delete command was executed successfully") ret = self.fs.remove(str(ceph_img_name).encode("utf-8")) return self.__return_success(ret) except FileSystemException as e: logger.exception('') self.iscsi.add_target(ceph_img_name) parent_name = self.fs.get_parent_info(ceph_img_name)[1] parent_id = self.db.image.fetch_id_with_name_from_project( parent_name, self.proj) self.db.image.insert(node_name, self.pid, parent_id, id=self.__extract_id(ceph_img_name)) time.sleep(constants.HIL_CALL_TIMEOUT) self.hil.attach_node_to_project_network(node_name, network, nic) return self.__return_error(e) except ISCSIException as e: logger.exception('') parent_name = self.fs.get_parent_info(ceph_img_name)[1] parent_id = self.db.image.fetch_id_with_name_from_project( parent_name, self.proj) self.db.image.insert(node_name, self.pid, parent_id, id=self.__extract_id(ceph_img_name)) time.sleep(constants.HIL_CALL_TIMEOUT) self.hil.attach_node_to_project_network(node_name, network, nic) return self.__return_error(e) except DBException as e: logger.exception('') time.sleep(constants.HIL_CALL_TIMEOUT) self.hil.attach_node_to_project_network(node_name, network, nic) return self.__return_error(e) except HILException as e: logger.exception('') return self.__return_error(e) # Creates snapshot for the given image with snap_name as given name # fs_obj will be populated by decorator @log def create_snapshot(self, node_name, snap_name): try: self.hil.validate_project(self.proj) ceph_img_name = self.__get_ceph_image_name(node_name) self.fs.snap_image(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.snap_protect(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME) parent_id = self.db.image.fetch_parent_id(self.proj, node_name) self.db.image.insert(snap_name, self.pid, parent_id, is_snapshot=True) snap_ceph_name = self.__get_ceph_image_name(snap_name) self.fs.clone(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME, snap_ceph_name) self.fs.flatten(snap_ceph_name) self.fs.snap_image(snap_ceph_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.snap_protect(snap_ceph_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.snap_unprotect(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.remove_snapshot(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME) return self.__return_success(True) except (HILException, DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) # Lists snapshot for the given image img_name # URL's have to be read from BMI config file # fs_obj will be populated by decorator @log def list_snapshots(self): try: self.hil.validate_project(self.proj) snapshots = self.db.image.fetch_snapshots_from_project(self.proj) return self.__return_success(snapshots) except (HILException, DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) # Removes snapshot snap_name for the given image img_name # fs_obj will be populated by decorator @log def remove_image(self, img_name): try: self.hil.validate_project(self.proj) ceph_img_name = self.__get_ceph_image_name(img_name) self.fs.snap_unprotect(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.remove_snapshot(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.remove(ceph_img_name) self.db.image.delete_with_name_from_project(img_name, self.proj) return self.__return_success(True) except (HILException, DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) # Lists the images for the project which includes the snapshot @log def list_images(self): try: self.hil.validate_project(self.proj) names = self.db.image.fetch_images_from_project(self.proj) return self.__return_success(names) except (HILException, DBException) as e: logger.exception('') return self.__return_error(e) @log def list_provisioned_nodes(self): try: clones = self.db.image.fetch_clones_from_project(self.proj) return self.__return_success(clones) except DBException as e: logger.exception('') return self.__return_error(e) @log def list_all_images(self): try: images = self.db.image.fetch_all_images() new_images = [] for image in images: image.insert( 3, self.get_ceph_image_name_from_project(image[1], image[2])) new_images.append(image) return self.__return_success(new_images) except DBException as e: logger.exception('') return self.__return_error(e) @log def import_ceph_image(self, img): """ Import an image from ceph to be used by BMI Clone an image in ceph to be used by BMI. :param img: Name of image in ceph :return: True on successful completion """ try: ceph_img_name = str(img) # create a snapshot of the golden image and protect it # this is needed because, in ceph, you can only create clones from # snapshots. self.fs.snap_image(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.snap_protect(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME) # insert golden image name into bmi db self.db.image.insert(ceph_img_name, self.pid) # get a name for our copy of the golden image. For instance an # image in ceph called centos6.7, after cloning, will be a given # a name like 4img1 based on the UID in config and image id in db snap_ceph_name = self.__get_ceph_image_name(ceph_img_name) # clone the snapshot of the golden image and then flatten it self.fs.clone(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME, snap_ceph_name) self.fs.flatten(snap_ceph_name) # create a snapshot of our newly created golden image so that when # we provision, we can easily make clones from this readily # available snapshot. self.fs.snap_image(snap_ceph_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.snap_protect(snap_ceph_name, constants.DEFAULT_SNAPSHOT_NAME) # unprotect and delete the snapshot of the original golden because # we no longer need it. self.fs.snap_unprotect(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.remove_snapshot(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME) return self.__return_success(True) except (DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) @log def import_ceph_snapshot(self, img, snap_name, protect): """ Import a snapshot from ceph to be used by BMI Clone a snapshot in ceph to be used by BMI. Similar to import_ceph_image except we can directly start the cloning process because it is already a snapshot. :param img: Name of snapshot in ceph :return: True on successful completion """ try: ceph_img_name = str(img) if protect: self.fs.snap_protect(ceph_img_name, snap_name) self.db.image.insert(ceph_img_name, self.pid) snap_ceph_name = self.__get_ceph_image_name(ceph_img_name) self.fs.clone(ceph_img_name, snap_name, snap_ceph_name) self.fs.flatten(snap_ceph_name) self.fs.snap_image(snap_ceph_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.snap_protect(snap_ceph_name, constants.DEFAULT_SNAPSHOT_NAME) return self.__return_success(True) except (DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) @log def export_ceph_image(self, img, name): try: ceph_img_name = self.__get_ceph_image_name(img) self.fs.clone(ceph_img_name, constants.DEFAULT_SNAPSHOT_NAME, name) self.fs.flatten(name) return self.__return_success(True) except FileSystemException as e: logger.exception('') return self.__return_error(e) @log def delete_image(self, project, img): try: if not self.is_admin: raise AuthorizationFailedException() self.db.image.delete_with_name_from_project(img, project) return self.__return_success(True) except (DBException, AuthorizationFailedException) as e: logger.exception('') return self.__return_error(e) @log def add_image(self, project, img, id, snap, parent, public): try: if not self.is_admin: raise AuthorizationFailedException() parent_id = None if parent is not None: parent_id = self.db.image.fetch_id_with_name_from_project( parent, project) pid = self.__does_project_exist(project) self.db.image.insert(img, pid, parent_id, public, snap, id) return self.__return_success(True) except (DBException, AuthorizationFailedException) as e: logger.exception('') return self.__return_error(e) @log def get_node_ip(self, node_name): try: mac_addr = self.hil.get_node_mac_addr(node_name) return self.__return_success(self.dhcp.get_ip(mac_addr)) except (HILException, DHCPException) as e: logger.exception('') return self.__return_error(e) @log def copy_image(self, img1, dest_project, img2=None): """ Create a deep copy of src image :param img1: Name of src image :param dest_project: Name of the project where des image will be created :param img2: Name of des image :return: True on successful completion """ try: if not self.is_admin and (self.proj != dest_project): raise AuthorizationFailedException() dest_pid = self.__does_project_exist(dest_project) self.db.image.copy_image(self.proj, img1, dest_pid, img2) if img2 is not None: ceph_name = self.get_ceph_image_name_from_project( img2, dest_project) else: ceph_name = self.get_ceph_image_name_from_project( img1, dest_project) self.fs.clone( self.get_ceph_image_name_from_project(img1, self.proj), constants.DEFAULT_SNAPSHOT_NAME, ceph_name) self.fs.flatten(ceph_name) self.fs.snap_image(ceph_name, constants.DEFAULT_SNAPSHOT_NAME) self.fs.snap_protect(ceph_name, constants.DEFAULT_SNAPSHOT_NAME) return self.__return_success(True) except (DBException, FileSystemException) as e: logger.exception('') return self.__return_error(e) @log def move_image(self, img1, dest_project, img2): try: if not self.is_admin and (self.proj != dest_project): raise AuthorizationFailedException() dest_pid = self.__does_project_exist(dest_project) self.db.image.move_image(self.proj, img1, dest_pid, img2) return self.__return_success(True) except DBException as e: logger.exception('') return self.__return_error(e) @log def add_project(self, project, network, id): try: if not self.is_admin: raise AuthorizationFailedException() self.db.project.insert(project, network, id) return self.__return_success(True) except (DBException, AuthorizationFailedException) as e: logger.exception('') return self.__return_error(e) @log def delete_project(self, project): try: if not self.is_admin: raise AuthorizationFailedException() self.db.project.delete_with_name(project) return self.__return_success(True) except (DBException, AuthorizationFailedException) as e: logger.exception('') return self.__return_error(e) @log def list_projects(self): try: if not self.is_admin: raise AuthorizationFailedException() projects = self.db.project.fetch_projects() return self.__return_success(projects) except (DBException, AuthorizationFailedException) as e: logger.exception('') return self.__return_error(e) @log def mount_image(self, img): try: if not self.is_admin: raise AuthorizationFailedException() ceph_img_name = self.__get_ceph_image_name(img) self.iscsi.add_target(ceph_img_name) return self.__return_success(True) except (ISCSIException, DBException) as e: logger.exception('') return self.__return_error(e) @log def umount_image(self, img): try: if not self.is_admin: raise AuthorizationFailedException() ceph_img_name = self.__get_ceph_image_name(img) self.iscsi.remove_target(ceph_img_name) return self.__return_success(True) except (ISCSIException, DBException) as e: logger.exception('') return self.__return_error(e) @log def show_mounted(self): try: if not self.is_admin: raise AuthorizationFailedException() mappings = self.iscsi.list_targets() swapped_mappings = {} for k, v in mappings.iteritems(): img_id = self.__extract_id(k) if self.proj == self.db.image.fetch_project_with_id(img_id): swapped_mappings[self.db.image.fetch_name_with_id( img_id)] = v return self.__return_success(swapped_mappings) except (ISCSIException, DBException) as e: logger.exception('') return self.__return_error(e) @log def remake_mappings(self): try: self.iscsi.persist_targets() except (FileSystemException, ISCSIException) as e: logger.exception('') except NotImplementedError: pass