Last active
August 29, 2015 14:04
-
-
Save feczo/4ddb551e09d6d3659629 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- move_cmds.py 2014-08-06 17:41:47.000000000 +1000 | |
+++ /Users/sub/cloud/platform/gcutil/lib/google_compute_engine/gcutil_lib/move_cmds.py 2014-08-06 18:21:24.000000000 +1000 | |
@@ -291,7 +291,7 @@ | |
self._VerifyOperations(self.MakeListResult(results, 'operationList')) | |
- def _DeleteInstances(self, instances, zone): | |
+ def _DeleteInstances(self, instances, zone, raisefatal=True): | |
"""Deletes the given instances. | |
Args: | |
@@ -317,9 +317,13 @@ | |
sleep_between_polls_seconds=self._GetSleepBetweenPollsSeconds(), | |
collection_name='instances') | |
if exceptions: | |
- raise gcutil_errors.CommandError( | |
+ args = ( | |
'Aborting due to errors while deleting instances:\n%s' % | |
utils.ListStrings(exceptions)) | |
+ if raisefatal: | |
+ raise gcutil_errors.CommandError(args) | |
+ else: | |
+ LOGGER.warn(args) | |
self._VerifyOperations(self.MakeListResult(results, 'operationList')) | |
def _MapPerZoneMachineType(self, machine_type, dst_zone): | |
@@ -1332,6 +1336,172 @@ | |
os.remove(log_path) | |
+class RecycleInstances(MoveInstancesBase): | |
+ """Bring a TERMINATED instance back to RUNNING again. | |
+ | |
+ Recycles a TERMINATED VM instance, resulting in retriveing | |
+ the configuration, deleting the instance than add the instance | |
+ again with the same configuration and disk. | |
+ | |
+ List of instances are expected to be in the same zone. | |
+ """ | |
+ | |
+ positional_args = '<instance-name-1> ... <instance-name-n>' | |
+ | |
+ def __init__(self, name, flag_values): | |
+ super(MoveInstancesBase, self).__init__(name, flag_values) | |
+ | |
+ flags.DEFINE_string( | |
+ 'input', | |
+ None, | |
+ '[Optional] The JSON instance resource file for addinstance.', | |
+ flag_values=flag_values) | |
+ | |
+ timestamp = datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S') | |
+ self._log_path = os.path.join(os.path.expanduser('~'), | |
+ '.gcutil.recycle.' + timestamp) | |
+ | |
+ def _GetInstanceResource(self, instance_name): | |
+ """Get the instance resource. This is the dictionary returned by the API. | |
+ | |
+ Args: | |
+ instance_name: The name of the instance to retrieve the ssh address for. | |
+ | |
+ Returns: | |
+ The data for the instance resource as returned by the API. | |
+ | |
+ Raises: | |
+ gcutil_errors.CommandError: If the instance does not exist. | |
+ """ | |
+ zone = self.GetZoneForResource(self.api.instances, | |
+ instance_name, | |
+ project=self._project) | |
+ request = self.api.instances.get(project=self._project, | |
+ zone=zone, | |
+ instance=instance_name) | |
+ result = request.execute() | |
+ if not result: | |
+ raise gcutil_errors.CommandError( | |
+ 'Unable to find the instance %s.' % (instance_name)) | |
+ | |
+ if result['status'] != 'TERMINATED': | |
+ raise gcutil_errors.CommandError( | |
+ 'Instance %s is not in TERMINATED state.\n' | |
+ 'You may use "gcutil ssh %s sudo poweroff" to terminate it\n' | |
+ 'or use "gcutil resetinstance %s" to reset it' % | |
+ tuple([instance_name]*3)) | |
+ return result | |
+ | |
+ def _CreateInstances(self, instances): | |
+ """Creates the instance resources based on JSON list. | |
+ | |
+ The instance resources have their ephemeral IPs are cleared. | |
+ | |
+ Args: | |
+ instances: A list of instance resources. | |
+ | |
+ Raises: | |
+ CommandError: If one or more of the insertions fail. | |
+ """ | |
+ if not instances: | |
+ return | |
+ | |
+ print 'Recycling instances ...' | |
+ | |
+ ip_addresses = [] | |
+ | |
+ ip_addresses = set(self._project_resource.get('externalIpAddresses', [])) | |
+ | |
+ region = self._zone[0:self._zone.rfind('-')] | |
+ addresses_json = self._address_list.get( | |
+ 'items', [])['regions/%s' % region].get('addresses', []) | |
+ ip_addresses = set(address['address'] for address in addresses_json) | |
+ | |
+ self._SetIps(instances, ip_addresses) | |
+ | |
+ requests = [] | |
+ for instance in instances: | |
+ requests.append(self.api.instances.insert( | |
+ project=self._project, body=instance, zone=self._zone)) | |
+ results, exceptions = self.ExecuteRequests( | |
+ requests, wait_for_operations=True, | |
+ timeout_seconds=self._GetTimeoutSeconds(), | |
+ sleep_between_polls_seconds=self._GetSleepBetweenPollsSeconds(), | |
+ collection_name='instances') | |
+ if exceptions: | |
+ raise gcutil_errors.CommandError( | |
+ 'Aborting due to errors while creating instances:\n%s' % | |
+ utils.ListStrings(exceptions)) | |
+ | |
+ def _DeleteLog(self): | |
+ """Deletes the log at log_path.""" | |
+ os.remove(self._log_path) | |
+ | |
+ def _WriteLog(self, instances): | |
+ with open(self._log_path, 'w') as f: | |
+ json.dump(instances, f) | |
+ | |
+ def Handle(self, *instance_names): | |
+ """The point of entry to the command. | |
+ | |
+ Args: | |
+ *instance_names: Names of the instances to recycle | |
+ | |
+ This dispatches the subclass' HandleMove method. | |
+ """ | |
+ if self.api.version != version.get('v1'): | |
+ raise gcutil_errors.UnsupportedCommand( | |
+ 'Recycling instances is supported only in service version v1.') | |
+ | |
+ self._project = utils.GetProjectId(self._project, self.api) | |
+ | |
+ self._project_resource = self.api.projects.get( | |
+ project=self._project).execute() | |
+ if self.api.addresses: | |
+ self._address_list = utils.AllAggregated( | |
+ self.api.addresses.aggregatedList, | |
+ self._project) | |
+ self.HandleRecycle(*instance_names) | |
+ print 'The recycle completed successfully.' | |
+ | |
+ def HandleRecycle(self, *instance_names): | |
+ """Initiate a delete/create cycle on the instance. | |
+ | |
+ Args: | |
+ *instance_names: The instance name to recycle. | |
+ | |
+ Returns: | |
+ An operation resource. | |
+ """ | |
+ | |
+ if self._flags.input: | |
+ with open(self._flags.input, 'r') as f: | |
+ instances = json.load(f) | |
+ f.close() | |
+ self._log_path = self._flags.input | |
+ else: | |
+ instances = [] | |
+ for instance_name in instance_names: | |
+ instances.append(self._GetInstanceResource(instance_name)) | |
+ self._WriteLog(instances) | |
+ | |
+ try: | |
+ print ('Do not worry the following operations ', | |
+ 'do not touch Disks so they are safe!') | |
+ self._zone = self.DenormalizeResourceName(instances[0]['zone']) | |
+ self._DeleteInstances(instances, self._zone, False) | |
+ result = self._CreateInstances(instances) | |
+ except Exception as e: | |
+ print 'Sorry, we encountered an error while recycling your instance.' | |
+ print 'Please retry the operation using the command: ' | |
+ print ' gcutil recycleinstances continue --input %s' % self._log_path | |
+ raise e | |
+ | |
+ self._DeleteLog() | |
+ return result | |
+ | |
+ | |
def AddCommands(): | |
appcommands.AddCmd('moveinstances', MoveInstances) | |
appcommands.AddCmd('resumemove', ResumeMove) | |
+ appcommands.AddCmd('recycleinstances', RecycleInstances) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment