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):
ap.add_argument('--buildout-debug',
action='store_true',
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.add_argument('--all', action='store_true',
help='Process all Computer Partitions.')
......
......@@ -294,7 +294,8 @@ def create_slapgrid_object(options, logger):
instance_storage_home=op.get('instance_storage_home'),
ipv4_global_network=op.get('ipv4_global_network'),
firewall_conf=op.get('firewall'),
config=options)
config=options,
force_stop=op.get('force_stop', False))
def check_required_only_partitions(existing, required):
......@@ -353,7 +354,8 @@ class Slapgrid(object):
firewall_conf={},
config=None,
buildout_debug=False,
shared_part_list=''
shared_part_list='',
force_stop=False,
):
"""Makes easy initialisation of class parameters"""
# Parses arguments
......@@ -422,6 +424,7 @@ class Slapgrid(object):
self.config = config
self._manager_list = slapmanager.from_config(config)
self.shared_part_list = shared_part_list
self.force_stop = force_stop
def _getWatchdogLine(self):
invocation_list = [WATCHDOG_PATH]
......@@ -1115,6 +1118,12 @@ stderr_logfile_backups=1
for manager in self._manager_list:
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.
# If not: it's not worth processing this partition (nothing has
# changed).
......@@ -1137,7 +1146,8 @@ stderr_logfile_backups=1
# should be processed at least every day.
if time.time() <= last_runtime + periodicity or periodicity < 0:
# 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._checkPromiseAnomaly(local_partition, computer_partition)
else:
......@@ -1188,12 +1198,16 @@ stderr_logfile_backups=1
if computer_partition_state == COMPUTER_PARTITION_STARTED_STATE:
local_partition.install()
local_partition.start()
if not self.force_stop:
local_partition.start()
else:
local_partition.stop()
if self.firewall_conf:
self._setupComputerPartitionFirewall(computer_partition,
partition_ip_list)
self._checkPromiseList(local_partition)
computer_partition.started()
if not self.force_stop:
self._checkPromiseList(local_partition)
computer_partition.started()
self._endInstallationTransaction(computer_partition)
elif computer_partition_state == COMPUTER_PARTITION_STOPPED_STATE:
try:
......
......@@ -151,7 +151,7 @@ class BasicMixin(object):
logging.basicConfig(level=logging.DEBUG)
self.setSlapgrid()
def setSlapgrid(self, develop=False):
def setSlapgrid(self, develop=False, force_stop=False):
if getattr(self, 'master_url', None) is None:
self.master_url = 'http://127.0.0.1:80/'
self.computer_id = 'computer'
......@@ -167,7 +167,8 @@ class BasicMixin(object):
self.buildout,
develop=develop,
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
# monkey patch buildout bootstrap
......@@ -199,8 +200,8 @@ class BasicMixin(object):
environment=USER="%(USER)s",LOGNAME="%(USER)s",HOME="%(HOME)s"
""")
def launchSlapgrid(self, develop=False):
self.setSlapgrid(develop=develop)
def launchSlapgrid(self, develop=False, force_stop=False):
self.setSlapgrid(develop=develop, force_stop=force_stop)
return self.grid.processComputerPartitionList()
def launchSlapgridSoftware(self, develop=False):
......@@ -1329,6 +1330,7 @@ class TestSlapgridCPWithMasterWatchdog(MasterMixin, unittest.TestCase):
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, [])
class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
def test_partition_timestamp(self):
......@@ -1773,6 +1775,36 @@ echo %s; echo %s; exit 42""" % (line1, line2))
dummyLogger.mock_calls[-1][1][0] % dummyLogger.mock_calls[-1][1][1:],
" 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):
"""
......
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