def test_create_standalone_accept_serial_ex(self, mrapi): """Create a volume, send commission and crash when accepting serial.""" with patch("synnefo.quotas.accept_serial") as m: m.side_effect = MurphysLaw with mocked_quotaholder() as mqh: volumes.create(**self.kwargs) # Assert that the transaction was commited and that the commission was # sent and accepted, albeit locally. vol = Volume.objects.get(name=self.volume_name) expected_commission = {(self.userid, "cyclades.disk"): self.size << 30} self.assertCommissionEqual(mqh, expected_commission) self.assertEqual(vol.serial.accept, True)
def test_create_and_attach_ex(self, mrapi): """Create a volume and crash when attaching it to a VM.""" self.kwargs["server_id"] = self.archip_vm.id mrapi().ModifyInstance.return_value = 42 with patch("synnefo.logic.backend.attach_volume") as m: m.side_effect = MurphysLaw with self.assertRaises(MurphysLaw): with mocked_quotaholder() as mqh: volumes.create(**self.kwargs) del self.kwargs["server_id"] # Assert that the commission was sent. expected_commission = {(self.userid, "cyclades.disk"): self.size << 30} self.assertCommissionEqual(mqh, expected_commission)
def test_create_standalone_create_common_ex(self, mrapi): """Create volume and crash right after that.""" def mock_create_common(*args, **kwargs): create_common(*args, **kwargs) raise MurphysLaw with patch.object(volumes, "create_common") as m: m.side_effect = mock_create_common with self.assertRaises(MurphysLaw): with mocked_quotaholder() as mqh: volumes.create(**self.kwargs) # Assert that no commission was sent self.assertNoCommission(mqh)
def test_create_standalone_handle_resource_commission_ex(self, mrapi): """Create a volume, send commission and crash right after that.""" def mock_handle_resource_commission(*args, **kwargs): handle_resource_commission(*args, **kwargs) raise MurphysLaw with patch("synnefo.quotas.handle_resource_commission") as m: m.side_effect = mock_handle_resource_commission with self.assertRaises(MurphysLaw): with mocked_quotaholder() as mqh: volumes.create(**self.kwargs) # Assert that the commission was sent. expected_commission = {(self.userid, "cyclades.disk"): self.size << 30} self.assertCommissionEqual(mqh, expected_commission)
def test_create_standalone_create_common_ex(self, mrapi): """Create volume and crash right after that.""" def mock_create_common(*args, **kwargs): create_common(*args, **kwargs) raise MurphysLaw with patch.object(volumes, "create_common") as m: m.side_effect = mock_create_common with self.assertRaises(MurphysLaw): with mocked_quotaholder() as mqh: volumes.create(**self.kwargs) # Assert that the transaction was rollbacked and that no commission was # sent with self.assertRaises(Volume.DoesNotExist): Volume.objects.get(name=self.volume_name) self.assertNoCommission(mqh)
def test_create_standalone(self, mrapi): """Test if standalone volumes are created properly.""" kwargs = self.create_kwargs(volume_type_id=self.archip_vt.id, server_id=None) with mocked_quotaholder() as m: vol = volumes.create(**kwargs) expected_commission = {(self.userid, "cyclades.disk"): self.size << 30} self.assertCommissionEqual(m, expected_commission) self.assertAcceptedSerial(m, vol.serial) self.common_volume_checks(vol) self.assertEqual(vol.machine, None) self.assertEqual(vol.volume_type, self.archip_vt) self.assertEqual(vol.status, "AVAILABLE") self.assertEqual(vol.index, None)
def test_create(self, mrapi): # No server id kwargs = deepcopy(self.kwargs) kwargs["server_id"] = None self.assertRaises(faults.BadRequest, volumes.create, **kwargs) # Invalid server vm = mf.VirtualMachineFactory(userid="other_user") kwargs["server_id"] = vm.id self.assertRaises(faults.BadRequest, volumes.create, **kwargs) # Invalid size kwargs = deepcopy(self.kwargs) max_size = settings.CYCLADES_VOLUME_MAX_SIZE kwargs["size"] = max_size + 1 self.assertRaises(faults.BadRequest, volumes.create, **kwargs) # Create server without source! mrapi().ModifyInstance.return_value = 42 with mocked_quotaholder(): vol = volumes.create(**self.kwargs) self.assertEqual(vol.size, self.size) self.assertEqual(vol.userid, self.userid) self.assertEqual(vol.name, None) self.assertEqual(vol.description, None) self.assertEqual(vol.source_snapshot_id, None) self.assertEqual(vol.source, None) self.assertEqual(vol.origin, None) self.assertEqual(vol.source_volume_id, None) self.assertEqual(vol.source_image_id, None) self.assertEqual(vol.machine, self.vm) self.assertEqual(vol.volume_type, self.vm.flavor.volume_type) name, args, kwargs = mrapi().ModifyInstance.mock_calls[0] self.assertEqual(kwargs["instance"], self.vm.backend_vm_id) disk_info = kwargs["disks"][0][2] self.assertEqual(disk_info["size"], self.size << 10) self.assertEqual(disk_info["name"], vol.backend_volume_uuid) self.assertFalse("origin" in disk_info)
def test_create_from_snapshot(self, mimage, mrapi): # Wrong source mimage().__enter__().get_snapshot.side_effect = faults.ItemNotFound kwargs = self.create_kwargs(server_id=self.archip_vm.id, source_snapshot_id=421) self.assertRaises(faults.BadRequest, volumes.create, **kwargs) mimage().__enter__().get_snapshot.side_effect = None mimage().__enter__().get_snapshot.return_value = { 'location': 'pithos://foo', 'mapfile': 'snf-snapshot-43', 'id': 12, 'name': "test_image", 'version': 42, 'size': 1242, 'disk_format': 'diskdump', 'status': 'AVAILABLE', 'properties': { 'source_volume': 42 } } mrapi().ModifyInstance.return_value = 42 kwargs = self.create_kwargs(source_snapshot_id=12, server_id=self.archip_vm.id) with mocked_quotaholder(): vol = volumes.create(**kwargs) self.assertEqual(vol.size, self.size) self.assertEqual(vol.userid, self.userid) self.assertEqual(vol.name, None) self.assertEqual(vol.description, None) self.assertEqual(int(vol.source_snapshot_id), 12) self.assertEqual(vol.source_volume_id, None) self.assertEqual(vol.source_image_id, None) self.assertEqual(vol.origin, "snf-snapshot-43") name, args, kwargs = mrapi().ModifyInstance.mock_calls[0] self.assertEqual(kwargs["instance"], self.archip_vm.backend_vm_id) disk_info = kwargs["disks"][0][2] self.assertEqual(disk_info["size"], self.size << 10) self.assertEqual(disk_info["name"], vol.backend_volume_uuid) self.assertEqual(disk_info["origin"], "snf-snapshot-43")
def create_and_attach(self, mrapi, vm): """Common tests for create and attach operation.""" kwargs = self.create_kwargs(server_id=vm.id) mrapi().ModifyInstance.return_value = 42 with mocked_quotaholder() as m: vol = volumes.create(**kwargs) expected_commission = {(self.userid, "cyclades.disk"): self.size << 30} self.assertCommissionEqual(m, expected_commission) self.common_volume_checks(vol) self.assertEqual(vol.machine, vm) self.assertEqual(vol.volume_type, vm.flavor.volume_type) self.assertEqual(vol.index, 0) gnt_args = self.get_ganeti_args(mrapi) self.assertEqual(gnt_args["instance"], vm.backend_vm_id) disk_info = self.get_ganeti_disk_args(mrapi)[2] self.assertEqual(disk_info["size"], self.size << 10) self.assertEqual(disk_info["name"], vol.backend_volume_uuid) self.assertFalse("origin" in disk_info)
def test_create_from_snapshot(self, mimage, mrapi): # Wrong source mimage().__enter__().get_snapshot.side_effect = faults.ItemNotFound kwargs = self.create_kwargs(server_id=self.archip_vm.id, source_snapshot_id=421) self.assertRaises(faults.BadRequest, volumes.create, **kwargs) mimage().__enter__().get_snapshot.side_effect = None mimage().__enter__().get_snapshot.return_value = { 'location': 'pithos://foo', 'mapfile': 'snf-snapshot-43', 'id': 12, 'name': "test_image", 'version': 42, 'size': 1242, 'disk_format': 'diskdump', 'status': 'AVAILABLE', 'properties': {'source_volume': 42}} mrapi().ModifyInstance.return_value = 42 kwargs = self.create_kwargs(source_snapshot_id=12, server_id=self.archip_vm.id) with mocked_quotaholder(): vol = volumes.create(**kwargs) self.assertEqual(vol.size, self.size) self.assertEqual(vol.userid, self.userid) self.assertEqual(vol.name, None) self.assertEqual(vol.description, None) self.assertEqual(int(vol.source_snapshot_id), 12) self.assertEqual(vol.source_volume_id, None) self.assertEqual(vol.source_image_id, None) self.assertEqual(vol.origin, "snf-snapshot-43") name, args, kwargs = mrapi().ModifyInstance.mock_calls[0] self.assertEqual(kwargs["instance"], self.archip_vm.backend_vm_id) disk_info = kwargs["disks"][0][2] self.assertEqual(disk_info["size"], self.size << 10) self.assertEqual(disk_info["name"], vol.backend_volume_uuid) self.assertEqual(disk_info["origin"], "snf-snapshot-43")
def test_create_bad_volume_types(self, mrapi): """Various tests for the create action regarding volume types.""" # No volume type kwargs = self.create_kwargs(server_id=None) with self.assertRaises(faults.BadRequest): volumes.create(**kwargs) # Conflicting volume types (ext_archipelago != file) conflict_msg = "Cannot create a volume with template '{}' to a " \ "server with volume template '{}'".format( self.archip_vt.template, self.file_vt.template) kwargs = self.create_kwargs(volume_type_id=self.archip_vt.id, server_id=self.file_vm.id) with self.assertRaisesMessage(faults.BadRequest, conflict_msg): volumes.create(**kwargs) # Non-detachable volume type non_detachable_msg = "Volume type 'file' is not detachable" kwargs = self.create_kwargs(volume_type_id=self.file_vt.id, server_id=None) with self.assertRaisesMessage(faults.BadRequest, non_detachable_msg): volumes.create(**kwargs)
def create_volume(request): """Create a new Volume.""" req = utils.get_json_body(request) credentials = request.credentials user_id = credentials.userid log.debug("User: %s, Action: create_volume, Request: %s", user_id, req) vol_dict = utils.get_attribute(req, "volume", attr_type=dict, required=True) name = utils.get_attribute(vol_dict, "display_name", attr_type=basestring, required=True) # Get and validate 'size' parameter size = utils.get_attribute(vol_dict, "size", attr_type=(basestring, int), required=True) try: size = int(size) if size <= 0: raise ValueError except (TypeError, ValueError): raise faults.BadRequest("Volume 'size' needs to be a positive integer" " value. '%s' cannot be accepted." % size) project = vol_dict.get("project") if project is None: project = user_id shared_to_project= vol_dict.get("shared_to_project", False) # Optional parameters volume_type_id = utils.get_attribute(vol_dict, "volume_type", attr_type=(basestring, int), required=False) description = utils.get_attribute(vol_dict, "display_description", attr_type=basestring, required=False, default="") metadata = utils.get_attribute(vol_dict, "metadata", attr_type=dict, required=False, default={}) # Id of the volume to clone from source_volume_id = utils.get_attribute(vol_dict, "source_volid", required=False) # Id of the snapshot to create the volume from source_snapshot_id = utils.get_attribute(vol_dict, "snapshot_id", required=False) snapshots_enabled = util.snapshots_enabled_for_user(request.user) if source_snapshot_id and not snapshots_enabled: raise faults.NotImplemented("Making a clone from a snapshot is not" " implemented") # Reference to an Image stored in Glance source_image_id = utils.get_attribute(vol_dict, "imageRef", required=False) # Get server ID to attach the volume. server_id = utils.get_attribute(vol_dict, "server_id", required=False) # Create the volume volume = volumes.create(credentials, size=size, name=name, source_volume_id=source_volume_id, source_snapshot_id=source_snapshot_id, source_image_id=source_image_id, volume_type_id=volume_type_id, description=description, metadata=metadata, server_id=server_id, project_id=project, shared_to_project=shared_to_project) log.info("User %s created volume %s attached to server %s, shared: %s", user_id, volume.id, server_id, shared_to_project) # Render response data = json.dumps(dict(volume=volume_to_dict(volume, detail=False))) return HttpResponse(data, status=202)
def handle(self, *args, **options): if args: raise CommandError("Command doesn't accept any arguments") size = options.get("size") user_id = options.get("user_id") project_id = options.get("project") server_id = options.get("server_id") volume_type_id = options.get("volume_type_id") wait = parse_bool(options["wait"]) credentials = Credentials(user_id) display_name = options.get("name", "") display_description = options.get("description", "") if size is None: raise CommandError("Please specify the size of the volume") if server_id: vm = common.get_resource("server", server_id) if project_id is None: project_id = vm.project if user_id is None and server_id is None: raise CommandError("Please specify the id of a user or a server") elif user_id is None and server_id is not None: user_id = vm.userid if volume_type_id is not None: vtype = common.get_resource("volume-type", volume_type_id) elif server_id: vtype = vm.flavor.volume_type else: raise CommandError("Please specify the id of the volume type") # At this point, user_id, vtype must have been provided or extracted by # the server. The project_id is optional and will default to the user's # project. source_image_id = source_volume_id = source_snapshot_id = None source = options.get("source") if source is not None: try: source_type, source_uuid = source.split(":", 1) except (ValueError, TypeError): raise CommandError("Invalid '--source' option. Value must be" " of the form <source_type>:<source_uuid>") if source_type == "image": source_image_id = source_uuid elif source_type == "snapshot": source_snapshot_id = source_uuid else: raise CommandError("Unknown volume source type '%s'" % source_type) volume = volumes.create(credentials, size, server_id, name=display_name, description=display_description, source_image_id=source_image_id, source_snapshot_id=source_snapshot_id, source_volume_id=source_volume_id, volume_type_id=vtype.id, metadata={}, project_id=project_id) self.stdout.write("Created volume '%s' in DB:\n" % volume.id) pprint.pprint_volume(volume, stdout=self.stdout) self.stdout.write("\n") if volume.machine is not None: volume.machine.task_job_id = volume.backendjobid common.wait_server_task(volume.machine, wait, stdout=self.stdout)
def handle(self, *args, **options): if args: raise CommandError("Command doesn't accept any arguments") size = options.get("size") user_id = options.get("user_id") project_id = options.get("project") server_id = options.get("server_id") volume_type_id = options.get("volume_type_id") wait = parse_bool(options["wait"]) display_name = options.get("name", "") display_description = options.get("description", "") if size is None: raise CommandError("Please specify the size of the volume") if server_id is None: raise CommandError("Please specify the server to attach the" " volume.") vm = common.get_resource("server", server_id, for_update=True) if user_id is None: user_id = vm.userid if volume_type_id is not None: vtype = common.get_resource("volume-type", volume_type_id) else: vtype = vm.flavor.volume_type if project_id is None: project_id = vm.project source_image_id = source_volume_id = source_snapshot_id = None source = options.get("source") if source is not None: try: source_type, source_uuid = source.split(":", 1) except (ValueError, TypeError): raise CommandError("Invalid '--source' option. Value must be" " of the form <source_type>:<source_uuid>") if source_type == "image": source_image_id = source_uuid elif source_type == "snapshot": source_snapshot_id = source_uuid else: raise CommandError("Unknown volume source type '%s'" % source_type) volume = volumes.create(user_id, size, server_id, name=display_name, description=display_description, source_image_id=source_image_id, source_snapshot_id=source_snapshot_id, source_volume_id=source_volume_id, volume_type_id=vtype.id, metadata={}, project=project_id) self.stdout.write("Created volume '%s' in DB:\n" % volume.id) pprint.pprint_volume(volume, stdout=self.stdout) self.stdout.write("\n") if volume.machine is not None: volume.machine.task_job_id = volume.backendjobid common.wait_server_task(volume.machine, wait, stdout=self.stdout)
def create_volume(request): """Create a new Volume.""" req = utils.get_json_body(request) log.debug("create_volume %s", req) user_id = request.user_uniq vol_dict = utils.get_attribute(req, "volume", attr_type=dict, required=True) name = utils.get_attribute(vol_dict, "display_name", attr_type=basestring, required=True) # Get and validate 'size' parameter size = utils.get_attribute(vol_dict, "size", attr_type=(basestring, int), required=True) try: size = int(size) if size <= 0: raise ValueError except (TypeError, ValueError): raise faults.BadRequest("Volume 'size' needs to be a positive integer" " value. '%s' cannot be accepted." % size) project = vol_dict.get("project") if project is None: project = user_id # Optional parameters volume_type_id = utils.get_attribute(vol_dict, "volume_type", attr_type=(basestring, int), required=False) description = utils.get_attribute(vol_dict, "display_description", attr_type=basestring, required=False, default="") metadata = utils.get_attribute(vol_dict, "metadata", attr_type=dict, required=False, default={}) # Id of the volume to clone from source_volume_id = utils.get_attribute(vol_dict, "source_volid", required=False) # Id of the snapshot to create the volume from source_snapshot_id = utils.get_attribute(vol_dict, "snapshot_id", required=False) if source_snapshot_id and not settings.CYCLADES_SNAPSHOTS_ENABLED: raise faults.NotImplemented("Making a clone from a snapshot is not" " implemented") # Reference to an Image stored in Glance source_image_id = utils.get_attribute(vol_dict, "imageRef", required=False) # Get server ID to attach the volume. Since we currently do not supported # detached volumes, server_id attribute is mandatory. server_id = utils.get_attribute(vol_dict, "server_id", required=True) # Create the volume volume = volumes.create(user_id=user_id, size=size, name=name, source_volume_id=source_volume_id, source_snapshot_id=source_snapshot_id, source_image_id=source_image_id, volume_type_id=volume_type_id, description=description, metadata=metadata, server_id=server_id, project=project) # Render response data = json.dumps(dict(volume=volume_to_dict(volume, detail=False))) return HttpResponse(data, status=202)
def create_volume(request): """Create a new Volume.""" req = utils.get_json_body(request) user_id = request.user_uniq log.debug("User: %s, Action: create_volume, Request: %s", user_id, req) vol_dict = utils.get_attribute(req, "volume", attr_type=dict, required=True) name = utils.get_attribute(vol_dict, "display_name", attr_type=basestring, required=True) # Get and validate 'size' parameter size = utils.get_attribute(vol_dict, "size", attr_type=(basestring, int), required=True) try: size = int(size) if size <= 0: raise ValueError except (TypeError, ValueError): raise faults.BadRequest("Volume 'size' needs to be a positive integer" " value. '%s' cannot be accepted." % size) project = vol_dict.get("project") if project is None: project = user_id shared_to_project = vol_dict.get("shared_to_project", False) # Optional parameters volume_type_id = utils.get_attribute(vol_dict, "volume_type", attr_type=(basestring, int), required=False) description = utils.get_attribute(vol_dict, "display_description", attr_type=basestring, required=False, default="") metadata = utils.get_attribute(vol_dict, "metadata", attr_type=dict, required=False, default={}) # Id of the volume to clone from source_volume_id = utils.get_attribute(vol_dict, "source_volid", required=False) # Id of the snapshot to create the volume from source_snapshot_id = utils.get_attribute(vol_dict, "snapshot_id", required=False) snapshots_enabled = util.snapshots_enabled_for_user(request.user) if source_snapshot_id and not snapshots_enabled: raise faults.NotImplemented("Making a clone from a snapshot is not" " implemented") # Reference to an Image stored in Glance source_image_id = utils.get_attribute(vol_dict, "imageRef", required=False) # Get server ID to attach the volume. server_id = utils.get_attribute(vol_dict, "server_id", required=False) server = None if server_id: try: server = get_vm(server_id, user_id, request.user_projects, for_update=True, non_deleted=True) except faults.ItemNotFound: raise faults.BadRequest("Server %s not found" % server_id) # Create the volume volume = volumes.create(user_id=user_id, size=size, name=name, source_volume_id=source_volume_id, source_snapshot_id=source_snapshot_id, source_image_id=source_image_id, volume_type_id=volume_type_id, description=description, metadata=metadata, server=server, project_id=project, shared_to_project=shared_to_project) server_id = server.id if server else None log.info("User %s created volume %s attached to server %s, shared: %s", user_id, volume.id, server_id, shared_to_project) # Render response data = json.dumps(dict(volume=volume_to_dict(volume, detail=False))) return HttpResponse(data, status=202)