Skip to content

Instantly share code, notes, and snippets.

@feczo
Last active August 29, 2015 14:04
Show Gist options
  • Save feczo/4ddb551e09d6d3659629 to your computer and use it in GitHub Desktop.
Save feczo/4ddb551e09d6d3659629 to your computer and use it in GitHub Desktop.
--- 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