Commit 01eaccf5 authored by Xavier Thompson's avatar Xavier Thompson

slapgrid: Add --force-stop option

This option applies only the processing of partitions as in:
  `slapos node instance --force-stop`

This option affects how `started` partition are processed:
- buildout is processed normally
- the services are stopped instead of started
- no promises are run
- no report is sent to master

See merge request nexedi/slapos.core!298
parent 1eb96dba
...@@ -157,6 +157,9 @@ class InstanceCommand(SlapgridCommand): ...@@ -157,6 +157,9 @@ class InstanceCommand(SlapgridCommand):
ap.add_argument('--buildout-debug', ap.add_argument('--buildout-debug',
action='store_true', action='store_true',
help='Run buildout in debug mode (with -D command line switch)') help='Run buildout in debug mode (with -D command line switch)')
ap.add_argument('--force-stop',
action='store_true',
help='Stop the services even for instances requested as started')
only = ap.add_mutually_exclusive_group() only = ap.add_mutually_exclusive_group()
only.add_argument('--all', action='store_true', only.add_argument('--all', action='store_true',
help='Process all Computer Partitions.') help='Process all Computer Partitions.')
......
...@@ -294,7 +294,8 @@ def create_slapgrid_object(options, logger): ...@@ -294,7 +294,8 @@ def create_slapgrid_object(options, logger):
instance_storage_home=op.get('instance_storage_home'), instance_storage_home=op.get('instance_storage_home'),
ipv4_global_network=op.get('ipv4_global_network'), ipv4_global_network=op.get('ipv4_global_network'),
firewall_conf=op.get('firewall'), firewall_conf=op.get('firewall'),
config=options) config=options,
force_stop=op.get('force_stop', False))
def check_required_only_partitions(existing, required): def check_required_only_partitions(existing, required):
...@@ -353,7 +354,8 @@ class Slapgrid(object): ...@@ -353,7 +354,8 @@ class Slapgrid(object):
firewall_conf={}, firewall_conf={},
config=None, config=None,
buildout_debug=False, buildout_debug=False,
shared_part_list='' shared_part_list='',
force_stop=False,
): ):
"""Makes easy initialisation of class parameters""" """Makes easy initialisation of class parameters"""
# Parses arguments # Parses arguments
...@@ -422,6 +424,7 @@ class Slapgrid(object): ...@@ -422,6 +424,7 @@ class Slapgrid(object):
self.config = config self.config = config
self._manager_list = slapmanager.from_config(config) self._manager_list = slapmanager.from_config(config)
self.shared_part_list = shared_part_list self.shared_part_list = shared_part_list
self.force_stop = force_stop
def _getWatchdogLine(self): def _getWatchdogLine(self):
invocation_list = [WATCHDOG_PATH] invocation_list = [WATCHDOG_PATH]
...@@ -1115,6 +1118,12 @@ stderr_logfile_backups=1 ...@@ -1115,6 +1118,12 @@ stderr_logfile_backups=1
for manager in self._manager_list: for manager in self._manager_list:
manager.instance(local_partition) manager.instance(local_partition)
# Since --force-stop option alters the processing of started partitions:
# - the partition should be processed regardless of the previous timestamp
# - the timestamp should not be updated
if self.force_stop and computer_partition_state == COMPUTER_PARTITION_STARTED_STATE:
timestamp = None
# Check if timestamp from server is more recent than local one. # Check if timestamp from server is more recent than local one.
# If not: it's not worth processing this partition (nothing has # If not: it's not worth processing this partition (nothing has
# changed). # changed).
...@@ -1137,7 +1146,8 @@ stderr_logfile_backups=1 ...@@ -1137,7 +1146,8 @@ stderr_logfile_backups=1
# should be processed at least every day. # should be processed at least every day.
if time.time() <= last_runtime + periodicity or periodicity < 0: if time.time() <= last_runtime + periodicity or periodicity < 0:
# check promises anomaly # check promises anomaly
if computer_partition_state == COMPUTER_PARTITION_STARTED_STATE: if (computer_partition_state == COMPUTER_PARTITION_STARTED_STATE
and not self.force_stop):
self.logger.debug('Partition already up-to-date.') self.logger.debug('Partition already up-to-date.')
self._checkPromiseAnomaly(local_partition, computer_partition) self._checkPromiseAnomaly(local_partition, computer_partition)
else: else:
...@@ -1188,10 +1198,14 @@ stderr_logfile_backups=1 ...@@ -1188,10 +1198,14 @@ stderr_logfile_backups=1
if computer_partition_state == COMPUTER_PARTITION_STARTED_STATE: if computer_partition_state == COMPUTER_PARTITION_STARTED_STATE:
local_partition.install() local_partition.install()
if not self.force_stop:
local_partition.start() local_partition.start()
else:
local_partition.stop()
if self.firewall_conf: if self.firewall_conf:
self._setupComputerPartitionFirewall(computer_partition, self._setupComputerPartitionFirewall(computer_partition,
partition_ip_list) partition_ip_list)
if not self.force_stop:
self._checkPromiseList(local_partition) self._checkPromiseList(local_partition)
computer_partition.started() computer_partition.started()
self._endInstallationTransaction(computer_partition) self._endInstallationTransaction(computer_partition)
......
...@@ -151,7 +151,7 @@ class BasicMixin(object): ...@@ -151,7 +151,7 @@ class BasicMixin(object):
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
self.setSlapgrid() self.setSlapgrid()
def setSlapgrid(self, develop=False): def setSlapgrid(self, develop=False, force_stop=False):
if getattr(self, 'master_url', None) is None: if getattr(self, 'master_url', None) is None:
self.master_url = 'http://127.0.0.1:80/' self.master_url = 'http://127.0.0.1:80/'
self.computer_id = 'computer' self.computer_id = 'computer'
...@@ -167,7 +167,8 @@ class BasicMixin(object): ...@@ -167,7 +167,8 @@ class BasicMixin(object):
self.buildout, self.buildout,
develop=develop, develop=develop,
logger=logging.getLogger(), logger=logging.getLogger(),
shared_part_list=self.shared_parts_root) shared_part_list=self.shared_parts_root,
force_stop=force_stop)
self.grid._manager_list = self.manager_list self.grid._manager_list = self.manager_list
# monkey patch buildout bootstrap # monkey patch buildout bootstrap
...@@ -199,8 +200,8 @@ class BasicMixin(object): ...@@ -199,8 +200,8 @@ class BasicMixin(object):
environment=USER="%(USER)s",LOGNAME="%(USER)s",HOME="%(HOME)s" environment=USER="%(USER)s",LOGNAME="%(USER)s",HOME="%(HOME)s"
""") """)
def launchSlapgrid(self, develop=False): def launchSlapgrid(self, develop=False, force_stop=False):
self.setSlapgrid(develop=develop) self.setSlapgrid(develop=develop, force_stop=force_stop)
return self.grid.processComputerPartitionList() return self.grid.processComputerPartitionList()
def launchSlapgridSoftware(self, develop=False): def launchSlapgridSoftware(self, develop=False):
...@@ -1329,6 +1330,7 @@ class TestSlapgridCPWithMasterWatchdog(MasterMixin, unittest.TestCase): ...@@ -1329,6 +1330,7 @@ class TestSlapgridCPWithMasterWatchdog(MasterMixin, unittest.TestCase):
watchdog.handle_event(headers, payload) watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, []) self.assertEqual(instance.sequence, [])
class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase): class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
def test_partition_timestamp(self): def test_partition_timestamp(self):
...@@ -1773,6 +1775,36 @@ echo %s; echo %s; exit 42""" % (line1, line2)) ...@@ -1773,6 +1775,36 @@ echo %s; echo %s; exit 42""" % (line1, line2))
dummyLogger.mock_calls[-1][1][0] % dummyLogger.mock_calls[-1][1][1:], dummyLogger.mock_calls[-1][1][0] % dummyLogger.mock_calls[-1][1][1:],
" 2[(not ready)]: Promise 'failing_promise' failed with output: fake promise error") " 2[(not ready)]: Promise 'failing_promise' failed with output: fake promise error")
def test_partition_force_stop(self):
"""
Launch slapgrid with --force-stop:
- buildout should be processed
- no timestamp should be generated
- services should be stopped
- no report should be sent to master
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
instance.timestamp = str(int(time.time()))
promise_ran = os.path.join(instance.partition_path, 'promise_ran')
promise = textwrap.dedent("""\
#!/usr/bin/env sh
touch "%s"
exit 127""" % promise_ran)
instance.setPromise('promise_script', promise)
self.assertEqual(self.launchSlapgrid(force_stop=True), slapgrid.SLAPGRID_SUCCESS)
six.assertCountEqual(self,
os.listdir(instance.partition_path),
['etc', '.slapgrid', 'buildout.cfg', 'software_release', 'worked', '.slapos-retention-lock-delay']
)
self.assertFalse(os.path.exists(promise_ran))
self.assertFalse(instance.sequence)
class TestSlapgridUsageReport(MasterMixin, unittest.TestCase): class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
""" """
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment