def CleanUp(project_id, zone, instance_name): """Clean up GCP project. Remove the instance [instance_name] in the GCP project [project_id] and its disks that were created as part of the end to end test. Attributes: project_id (str): the project id of the GCP project. zone (str): the zone for the project. instance_name (str): the name of the analysis VM to remove. """ gcp_client = common.GoogleCloudComputeClient(project_id=project_id) project = gcp_project.GoogleCloudProject(project_id, zone) disks = compute.GoogleComputeInstance(project.project_id, zone, instance_name).ListDisks() # delete the created forensics VMs log.info('Deleting analysis instance: {0:s}.'.format(instance_name)) gce_instances_client = gcp_client.GceApi().instances() request = gce_instances_client.delete(project=project.project_id, zone=project.default_zone, instance=instance_name) try: request.execute() except HttpError: # GceOperation triggers a while(True) loop that checks on the # operation ID. Sometimes it loops one more time right when the # operation has finished and thus the associated ID doesn't exists # anymore, throwing an HttpError. We can ignore this. pass log.info('Instance {0:s} successfully deleted.'.format(instance_name)) # delete the copied disks # we ignore the disk that was created for the analysis VM (disks[0]) as # it is deleted in the previous operation gce_disks_client = gcp_client.GceApi().disks() for disk in list(disks.keys())[1:]: log.info('Deleting disk: {0:s}.'.format(disk)) while True: try: request = gce_disks_client.delete(project=project.project_id, zone=project.default_zone, disk=disk) request.execute() break except HttpError as exception: # GceApi() will throw a 400 error until the analysis VM deletion is # correctly propagated. When the disk is finally deleted, it will # throw a 404 not found if it looped again after deletion. if exception.resp.status == 404: break if exception.resp.status != 400: log.warning( 'Could not delete the disk {0:s}: {1:s}'.format( disk, str(exception))) # Throttle the requests to one every 10 seconds time.sleep(10) log.info('Disk {0:s} successfully deleted.'.format(disk))
def test_end_to_end_boot_disk(self): """End to end test on GCP. This end-to-end test runs directly on GCP and tests that: 1. The project.py module connects to the target instance and makes a snapshot of the boot disk. 2. A new disk is created from the taken snapshot. 3. If an analysis VM already exists, the module will attach the disk copy to the VM. Otherwise, it will create a new GCP instance for analysis purpose and attach the boot disk copy to it. """ # Make a copy of the boot disk of the instance to analyse self.boot_disk_copy = forensics.CreateDiskCopy( src_proj=self.project_id, dst_proj=self.project_id, instance_name=self.instance_to_analyse, zone=self.zone, # disk_name=None by default, boot disk will be copied ) gcp_client_api = common.GoogleCloudComputeClient(self.project_id).GceApi() # The disk copy should be attached to the analysis project gce_disk_client = gcp_client_api.disks() request = gce_disk_client.get( project=self.project_id, zone=self.zone, disk=self.boot_disk_copy.name) result = request.execute() self.assertEqual(result['name'], self.boot_disk_copy.name) # Get the analysis VM and attach the evidence boot disk self.analysis_vm, _ = forensics.StartAnalysisVm( project=self.project_id, vm_name=self.analysis_vm_name, zone=self.zone, boot_disk_size=10, boot_disk_type='pd-ssd', cpu_cores=4, attach_disks=[self.boot_disk_copy.name]) # The forensic instance should be live in the analysis GCP project and # the disk should be attached gce_instance_client = gcp_client_api.instances() request = gce_instance_client.get( project=self.project_id, zone=self.zone, instance=self.analysis_vm_name) result = request.execute() self.assertEqual(result['name'], self.analysis_vm_name) for disk in result['disks']: if disk['boot']: request = gce_disk_client.get(project=self.project_id, zone=self.zone, disk=disk['source'].split('/')[-1]) boot_disk = request.execute() self.assertEqual('pd-ssd', boot_disk['type'].rsplit('/', 1)[-1]) if disk['source'].split('/')[-1] == self.boot_disk_copy.name: return self.fail( 'Error: could not find the disk {0:s} in instance {1:s}'.format( self.boot_disk_copy.name, self.analysis_vm_name))
def tearDownClass(cls): analysis_vm = cls.analysis_vm zone = cls.zone project = cls.gcp disks = analysis_vm.ListDisks() gcp_client = common.GoogleCloudComputeClient(project.project_id) # delete the created forensics VMs common.LOGGER.info('Deleting analysis instance: {0:s}.'.format( analysis_vm.name)) gce_instance_client = gcp_client.GceApi().instances() request = gce_instance_client.delete(project=project.project_id, zone=zone, instance=analysis_vm.name) response = request.execute() try: gcp_client.BlockOperation(response) except HttpError: # BlockOperation triggers a while(True) loop that checks on the # operation ID. Sometimes it loops one more time right when the # operation has finished and thus the associated ID doesn't exists # anymore, throwing an HttpError. We can ignore this. pass common.LOGGER.info('Instance {0:s} successfully deleted.'.format( analysis_vm.name)) # delete the copied disks # we ignore the disk that was created for the analysis VM (disks[0]) as # it is deleted in the previous operation for disk in list(disks.keys())[1:]: common.LOGGER.info('Deleting disk: {0:s}.'.format(disk)) while True: try: gce_disk_client = gcp_client.GceApi().disks() request = gce_disk_client.delete( project=project.project_id, zone=zone, disk=disk) response = request.execute() gcp_client.BlockOperation(response) break except HttpError as exception: # The gce api will throw a 400 until the analysis vm's deletion is # correctly propagated. When the disk is finally deleted, it will # throw a 404 not found if it looped one more time after deletion. if exception.resp.status == 404: break if exception.resp.status != 400: common.LOGGER.warning( 'Could not delete the disk {0:s}: {1:s}'.format( disk, str(exception))) # Throttle the requests to one every 10 seconds time.sleep(10) common.LOGGER.info('Disk {0:s} successfully deleted.'.format(disk))
def __init__(self, *args, **kwargs): super(EndToEndTest, self).__init__(*args, **kwargs) try: project_info = ReadProjectInfo() except (OSError, RuntimeError, ValueError) as exception: self.error_msg = str(exception) return self.project_id = project_info['project_id'] self.instance_to_analyse = project_info['instance'] # Optional: test a disk other than the boot disk self.disk_to_forensic = project_info.get('disk', None) self.zone = project_info['zone'] self.gcp_client = common.GoogleCloudComputeClient( project_id=self.project_id)