def test_dump_configuration_busy(database_connection): magic_castle = MagicCastle(database_connection, "missingfloatingips.c3.ca") assert magic_castle.dump_configuration() == { "cluster_name": "missingfloatingips", "domain": "c3.ca", "image": "CentOS-7-x64-2019-07", "nb_users": 17, "instances": { "mgmt": { "type": "p4-6gb", "count": 1 }, "login": { "type": "p4-6gb", "count": 1 }, "node": { "type": "p2-3gb", "count": 3 }, }, "storage": { "type": "nfs", "home_size": 50, "project_size": 1, "scratch_size": 1, }, "public_keys": [""], "hieradata": "", "guest_passwd": "password-123", "os_floating_ips": ["Automatic allocation"], }
def test_get_status_valid(database_connection): created = MagicCastle(database_connection, "created.calculquebec.cloud") assert created.get_status() == ClusterStatusCode.CREATED buildplanning = MagicCastle(database_connection, "buildplanning.calculquebec.cloud") assert buildplanning.get_status() == ClusterStatusCode.PLAN_RUNNING valid1 = MagicCastle(database_connection, "valid1.calculquebec.cloud") assert valid1.get_status() == ClusterStatusCode.PROVISIONING_SUCCESS
def get_all_magic_castles(self): """ Retrieve all the Magic Castles retrieved in the database. :return: A list of MagicCastle objects """ results = self._database_connection.execute( "SELECT hostname FROM magic_castles").fetchall() return [MagicCastle(result[0]) for result in results]
def get_magic_castle_by_hostname(self, hostname): if self.is_admin(): results = self._database_connection.execute( "SELECT hostname, owner FROM magic_castles WHERE hostname = ?", (hostname, ), ) else: results = self._database_connection.execute( "SELECT hostname, owner FROM magic_castles WHERE owner = ? AND hostname = ?", (self.edu_person_principal_name, hostname), ) row = results.fetchone() if row: return MagicCastle(hostname=row[0], owner=row[1]) else: raise ClusterNotFoundException
def test_create_magic_castle_init_fail(database_connection, monkeypatch): def fake_run(process_args, *args, **kwargs): if process_args == ["terraform", "init", "-no-color", "-input=false"]: raise CalledProcessError(1, "terraform init") monkeypatch.setattr("models.magic_castle.magic_castle.run", fake_run) cluster = MagicCastle(database_connection, "a-123-45.calculquebec.cloud") cluster.set_configuration(VALID_CLUSTER_CONFIGURATION) with pytest.raises( PlanException, match="An error occurred while initializing Terraform."): cluster.plan_creation()
def get_all_magic_castles(self): """ If the user is admin, it will retrieve all the clusters, otherwise, only the clusters owned by the user. :return: A list of MagicCastle objects """ if self.is_admin(): results = self._database_connection.execute( "SELECT hostname, owner FROM magic_castles") else: results = self._database_connection.execute( "SELECT hostname, owner FROM magic_castles WHERE owner = ?", (self.edu_person_principal_name, ), ) return [ MagicCastle(hostname=result[0], owner=result[1]) for result in results.fetchall() ]
def test_create_magic_castle_plan_export_fail(database_connection, monkeypatch): def fake_run(process_args, *args, **kwargs): if process_args[:4] == [ "terraform", "show", "-no-color", "-json", ]: raise CalledProcessError(1, "terraform show") monkeypatch.setattr("models.magic_castle.magic_castle.run", fake_run) cluster = MagicCastle(database_connection, "a-123-45.calculquebec.cloud") cluster.set_configuration(VALID_CLUSTER_CONFIGURATION) with pytest.raises( PlanException, match="An error occurred while exporting planned changes."): cluster.plan_creation()
def create_empty_magic_castle(self): return MagicCastle(self._database_connection, owner=self.edu_person_principal_name)
def test_get_available_resources_missing_nodes(database_connection): """ Mock context : missingnodes cluster uses 0 instance 0 vcpus 0 ram 0 [root disks] + 3 [external volumes] = 3 volumes 0 + 0 + 0 [root disks] + 50 + 50 + 100 [external volumes] = 200 GiO of volume storage openstack's quotas says there currently remains: 128 - 28 = 100 instances 500 - 199 = 301 vcpus 286,720 - 184,320 = 102,400 ram (100 GiO) 128 - 100 = 28 volumes 1000 - 720 = 280 GiO of volume storage Therefore, missingnodes cluster can use a total of: 0 + 100 = 0 instances 0 + 301 = 301 vcpus 0 + 102,400 = 102,400 ram (100 GiO) 3 + 28 = 31 volumes 200 + 280 = 480 GiO of volume storage """ magic_castle = MagicCastle(database_connection, "missingnodes.sub.example.com") assert magic_castle.get_available_resources() == { "quotas": { "instance_count": { "max": 100 }, "ram": { "max": 102_400 }, "vcpus": { "max": 301 }, "volume_count": { "max": 31 }, "volume_size": { "max": 480 }, }, "resource_details": { "instance_types": [ { "name": "p1-1.5gb", "vcpus": 1, "ram": 1_536, "required_volume_count": 1, "required_volume_size": 10, }, { "name": "p2-3gb", "vcpus": 2, "ram": 3_072, "required_volume_count": 1, "required_volume_size": 10, }, { "name": "p4-6gb", "vcpus": 4, "ram": 6_144, "required_volume_count": 1, "required_volume_size": 10, }, { "name": "c8-30gb-186", "vcpus": 8, "ram": 30_720, "required_volume_count": 0, "required_volume_size": 0, }, { "name": "c8-90gb-186", "vcpus": 8, "ram": 92_160, "required_volume_count": 0, "required_volume_size": 0, }, { "name": "g2-c24-112gb-500", "vcpus": 24, "ram": 114_688, "required_volume_count": 0, "required_volume_size": 0, }, { "name": "c16-120gb-392", "vcpus": 16, "ram": 122_880, "required_volume_count": 0, "required_volume_size": 0, }, ] }, "possible_resources": { "image": ["centos7", "CentOS-8 x64", "CentOS VGPU"], "instances": { "mgmt": { "type": [ "p4-6gb", "c8-30gb-186", "c8-90gb-186", "g2-c24-112gb-500", "c16-120gb-392", ] }, "login": { "type": [ "p2-3gb", "p4-6gb", "c8-30gb-186", "c8-90gb-186", "g2-c24-112gb-500", "c16-120gb-392", ] }, "node": { "type": [ "p2-3gb", "p4-6gb", "c8-30gb-186", "c8-90gb-186", "g2-c24-112gb-500", "c16-120gb-392", ] }, }, "os_floating_ips": [ "Automatic allocation", "100.101.102.103", "2.1.1.1", "2.1.1.2", "2.1.1.3", ], "storage": { "type": ["nfs"] }, "domain": ["calculquebec.cloud", "c3.ca", "sub.example.com"], }, }
def test_dump_configuration_not_found(database_connection): magic_castle = MagicCastle(database_connection) with pytest.raises(ClusterNotFoundException): magic_castle.dump_configuration()
def test_create_magic_castle_plan_valid(database_connection): cluster = MagicCastle(database_connection, "a-123-45.calculquebec.cloud") cluster.set_configuration(VALID_CLUSTER_CONFIGURATION) cluster.plan_creation()
def test_dump_configuration_missing_nodes(database_connection): magic_castle = MagicCastle(database_connection, "missingnodes.sub.example.com") assert magic_castle.dump_configuration(planned_only=True) == { "cluster_name": "missingnodes", "nb_users": 10, "guest_passwd": "password-123", "storage": { "type": "nfs", "home_size": 100, "scratch_size": 50, "project_size": 50, }, "instances": { "mgmt": { "type": "p4-6gb", "count": 1 }, "login": { "type": "p4-6gb", "count": 1 }, "node": { "type": "p2-3gb", "count": 1 }, }, "domain": "sub.example.com", "hieradata": "", "public_keys": [""], "image": "CentOS-7-x64-2019-07", "os_floating_ips": ["100.101.102.103"], } assert magic_castle.dump_configuration(planned_only=False) == { "cluster_name": "missingnodes", "nb_users": 10, "guest_passwd": "password-123", "storage": { "type": "nfs", "home_size": 100, "scratch_size": 50, "project_size": 50, }, "instances": { "mgmt": { "type": "", "count": 0 }, "login": { "type": "", "count": 0 }, "node": { "type": "", "count": 0 }, }, "domain": "sub.example.com", "hieradata": "", "public_keys": ["ssh-rsa FAKE"], "image": "CentOS-7-x64-2019-07", "os_floating_ips": ["100.101.102.103"], }
def create_empty_magic_castle(self): return MagicCastle()
def test_get_owner_no_owner(database_connection): magic_castle = MagicCastle(database_connection, "noowner.calculquebec.cloud") assert magic_castle.get_owner() == None
def test_get_status_errors(database_connection): empty = MagicCastle(database_connection, "empty.calculquebec.cloud") assert empty.get_status() == ClusterStatusCode.BUILD_ERROR missingnodes = MagicCastle(database_connection, "missingnodes.sub.example.com") assert missingnodes.get_status() == ClusterStatusCode.BUILD_ERROR
def create_empty_magic_castle(self): return MagicCastle(self._database_connection)
def create_empty_magic_castle(self): return MagicCastle(owner=self.edu_person_principal_name)
def test_get_plan_type_destroy(database_connection): magic_castle = MagicCastle(database_connection, "valid1.calculquebec.cloud") assert magic_castle.get_plan_type() == PlanType.DESTROY
def test_get_plan_type_build(database_connection): build_planning = MagicCastle(database_connection, "buildplanning.calculquebec.cloud") assert build_planning.get_plan_type() == PlanType.BUILD created = MagicCastle(database_connection, "created.calculquebec.cloud") assert created.get_plan_type() == PlanType.BUILD
def test_get_status_not_found(database_connection): magic_castle1 = MagicCastle(database_connection, "nonexisting") assert magic_castle1.get_status() == ClusterStatusCode.NOT_FOUND magic_castle2 = MagicCastle(database_connection) assert magic_castle2.get_status() == ClusterStatusCode.NOT_FOUND
def test_get_available_resources_not_found(database_connection): """ Mock context : openstack's quotas says there currently remains: 128 - 28 = 100 instances 500 - 199 = 301 vcpus 286,720 - 184,320 = 102,400 ram (100 GiO) 128 - 100 = 28 volumes 1000 - 720 = 280 GiO of volume storage """ magic_castle = MagicCastle(database_connection) assert magic_castle.get_available_resources() == { "quotas": { "instance_count": { "max": 100 }, "ram": { "max": 102_400 }, "vcpus": { "max": 301 }, "volume_count": { "max": 28 }, "volume_size": { "max": 280 }, }, "resource_details": { "instance_types": [ { "name": "p1-1.5gb", "vcpus": 1, "ram": 1_536, "required_volume_count": 1, "required_volume_size": 10, }, { "name": "p2-3gb", "vcpus": 2, "ram": 3_072, "required_volume_count": 1, "required_volume_size": 10, }, { "name": "p4-6gb", "vcpus": 4, "ram": 6_144, "required_volume_count": 1, "required_volume_size": 10, }, { "name": "c8-30gb-186", "vcpus": 8, "ram": 30_720, "required_volume_count": 0, "required_volume_size": 0, }, { "name": "c8-90gb-186", "vcpus": 8, "ram": 92_160, "required_volume_count": 0, "required_volume_size": 0, }, { "name": "g2-c24-112gb-500", "vcpus": 24, "ram": 114_688, "required_volume_count": 0, "required_volume_size": 0, }, { "name": "c16-120gb-392", "vcpus": 16, "ram": 122_880, "required_volume_count": 0, "required_volume_size": 0, }, ] }, "possible_resources": { "image": ["centos7", "CentOS-8 x64", "CentOS VGPU"], "instances": { "mgmt": { "type": [ "p4-6gb", "c8-30gb-186", "c8-90gb-186", "g2-c24-112gb-500", "c16-120gb-392", ] }, "login": { "type": [ "p2-3gb", "p4-6gb", "c8-30gb-186", "c8-90gb-186", "g2-c24-112gb-500", "c16-120gb-392", ] }, "node": { "type": [ "p2-3gb", "p4-6gb", "c8-30gb-186", "c8-90gb-186", "g2-c24-112gb-500", "c16-120gb-392", ] }, }, "os_floating_ips": [ "Automatic allocation", "2.1.1.1", "2.1.1.2", "2.1.1.3", ], "storage": { "type": ["nfs"] }, "domain": ["calculquebec.cloud", "c3.ca", "sub.example.com"], }, }
def test_dump_configuration_empty(database_connection): magic_castle = MagicCastle(database_connection, "empty.calculquebec.cloud") assert magic_castle.dump_configuration() == dict()
def test_get_plan_type_none(database_connection): magic_castle = MagicCastle(database_connection, "missingfloatingips.c3.ca") assert magic_castle.get_plan_type() == PlanType.NONE
def get_magic_castle_by_hostname(self, hostname): return MagicCastle(hostname)
def get_magic_castle_by_hostname(self, hostname): return MagicCastle(self._database_connection, hostname)
def test_get_owner_valid(database_connection): magic_castle = MagicCastle(database_connection, "missingfloatingips.c3.ca") assert magic_castle.get_owner() == "*****@*****.**"