diff options
-rw-r--r-- | heat_cfntools/cfntools/cfn_helper.py | 96 | ||||
-rw-r--r-- | heat_cfntools/tests/test_cfn_helper.py | 211 |
2 files changed, 244 insertions, 63 deletions
diff --git a/heat_cfntools/cfntools/cfn_helper.py b/heat_cfntools/cfntools/cfn_helper.py index 6d0aa48..6f7574b 100644 --- a/heat_cfntools/cfntools/cfn_helper.py +++ b/heat_cfntools/cfntools/cfn_helper.py @@ -22,7 +22,6 @@ import atexit import ConfigParser import errno import grp -import hashlib import json import logging import os @@ -714,37 +713,47 @@ class ServicesHandler(object): self.hooks = hooks def _handle_sysv_command(self, service, command): - service_exe = "/sbin/service" - enable_exe = "/sbin/chkconfig" - cmd = "" - if "enable" == command: - cmd = "%s %s on" % (enable_exe, service) - elif "disable" == command: - cmd = "%s %s off" % (enable_exe, service) - elif "start" == command: - cmd = "%s %s start" % (service_exe, service) - elif "stop" == command: - cmd = "%s %s stop" % (service_exe, service) - elif "status" == command: - cmd = "%s %s status" % (service_exe, service) - command = CommandRunner(cmd) - command.run() - return command + if os.path.exists("/bin/systemctl"): + service_exe = "/bin/systemctl" + service = '%s.service' % service + service_start = '%s start %s' + service_status = '%s status %s' + service_stop = '%s stop %s' + elif os.path.exists("/sbin/service"): + service_exe = "/sbin/service" + service_start = '%s %s start' + service_status = '%s %s status' + service_stop = '%s %s stop' + else: + service_exe = "/usr/sbin/service" + service_start = '%s %s start' + service_status = '%s %s status' + service_stop = '%s %s stop' + + if os.path.exists("/bin/systemctl"): + enable_exe = "/bin/systemctl" + enable_on = '%s enable %s' + enable_off = '%s disable %s' + elif os.path.exists("/sbin/chkconfig"): + enable_exe = "/sbin/chkconfig" + enable_on = '%s %s on' + enable_off = '%s %s off' + else: + enable_exe = "/usr/sbin/update-rc.d" + enable_on = '%s %s enable' + enable_off = '%s %s disable' - def _handle_systemd_command(self, service, command): - exe = "/bin/systemctl" cmd = "" - service = '%s.service' % service if "enable" == command: - cmd = "%s enable %s" % (exe, service) + cmd = enable_on % (enable_exe, service) elif "disable" == command: - cmd = "%s disable %s" % (exe, service) + cmd = enable_off % (enable_exe, service) elif "start" == command: - cmd = "%s start %s" % (exe, service) + cmd = service_start % (service_exe, service) elif "stop" == command: - cmd = "%s stop %s" % (exe, service) + cmd = service_stop % (service_exe, service) elif "status" == command: - cmd = "%s status %s" % (exe, service) + cmd = service_status % (service_exe, service) command = CommandRunner(cmd) command.run() return command @@ -796,7 +805,7 @@ class ServicesHandler(object): # map of function pointers to various service handlers _service_handlers = { "sysvinit": _handle_sysv_command, - "systemd": _handle_systemd_command + "systemd": _handle_sysv_command } def _service_handler(self, manager_name): @@ -1179,6 +1188,11 @@ class Metadata(object): True -- success False -- error """ + if self.resource is not None: + res_last_path = last_path + '_' + self.resource + else: + res_last_path = last_path + if meta_str: self._data = meta_str else: @@ -1198,7 +1212,7 @@ class Metadata(object): # cached metadata or the logic below could re-run a stale # cfn-init-data fd = None - for filepath in [last_path, default_path]: + for filepath in [res_last_path, last_path, default_path]: try: fd = open(filepath) except IOError: @@ -1221,18 +1235,21 @@ class Metadata(object): else: self._metadata = self._data - cm = hashlib.md5(json.dumps(self._metadata)) - current_md5 = cm.hexdigest() - old_md5 = None + last_data = "" + for metadata_file in [res_last_path, last_path]: + try: + with open(metadata_file) as lm: + try: + last_data = json.load(lm) + except ValueError: + pass + lm.close() + except IOError: + LOG.warn("Unable to open local metadata : %s" % + metadata_file) + continue - try: - with open(last_path) as lm: - om = hashlib.md5() - om.update(lm.read()) - old_md5 = om.hexdigest() - except Exception: - pass - if old_md5 != current_md5: + if self._metadata != last_data: self._has_changed = True # if cache dir does not exist try to create it @@ -1252,6 +1269,9 @@ class Metadata(object): os.chmod(cf.name, 0o600) cf.write(json.dumps(self._metadata)) os.rename(cf.name, last_path) + cf.close() + if res_last_path != last_path: + shutil.copy(last_path, res_last_path) return True diff --git a/heat_cfntools/tests/test_cfn_helper.py b/heat_cfntools/tests/test_cfn_helper.py index bdda61f..49c7206 100644 --- a/heat_cfntools/tests/test_cfn_helper.py +++ b/heat_cfntools/tests/test_cfn_helper.py @@ -158,6 +158,8 @@ class TestPackages(MockPopenTestCase): class TestServicesHandler(MockPopenTestCase): def test_services_handler_systemd(self): + self.m.StubOutWithMock(os.path, 'exists') + os.path.exists('/bin/systemctl').MultipleTimes().AndReturn(True) # apply_services self.mock_cmd_run( ['su', 'root', '-c', '/bin/systemctl enable httpd.service'] @@ -234,6 +236,8 @@ class TestServicesHandler(MockPopenTestCase): self.m.VerifyAll() def test_services_handler_systemd_disabled(self): + self.m.StubOutWithMock(os.path, 'exists') + os.path.exists('/bin/systemctl').MultipleTimes().AndReturn(True) # apply_services self.mock_cmd_run( ['su', 'root', '-c', '/bin/systemctl disable httpd.service'] @@ -275,7 +279,11 @@ class TestServicesHandler(MockPopenTestCase): self.m.VerifyAll() - def test_services_handler_sysv(self): + def test_services_handler_sysv_service_chkconfig(self): + self.m.StubOutWithMock(os.path, 'exists') + os.path.exists('/bin/systemctl').MultipleTimes().AndReturn(False) + os.path.exists('/sbin/service').MultipleTimes().AndReturn(True) + os.path.exists('/sbin/chkconfig').MultipleTimes().AndReturn(True) # apply_services self.mock_cmd_run( ['su', 'root', '-c', '/sbin/chkconfig httpd on'] @@ -286,50 +294,118 @@ class TestServicesHandler(MockPopenTestCase): self.mock_cmd_run( ['su', 'root', '-c', '/sbin/service httpd start'] ).AndReturn(FakePOpen()) + + # monitor_services not running + self.mock_cmd_run( + ['su', 'root', '-c', '/sbin/service httpd status'] + ).AndReturn(FakePOpen(returncode=-1)) self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/chkconfig mysqld on'] + ['su', 'root', '-c', '/sbin/service httpd start'] ).AndReturn(FakePOpen()) self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service mysqld status'] - ).AndReturn(FakePOpen(returncode=-1)) + ['su', 'root', '-c', '/bin/services_restarted'] + ).AndReturn(FakePOpen()) + + # monitor_services running self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service mysqld start'] + ['su', 'root', '-c', '/sbin/service httpd status'] ).AndReturn(FakePOpen()) - # monitor_services not running + self.m.ReplayAll() + + services = { + "sysvinit": { + "httpd": {"enabled": "true", "ensureRunning": "true"} + } + } + hooks = [ + cfn_helper.Hook( + 'hook1', + 'service.restarted', + 'Resources.resource1.Metadata', + 'root', + '/bin/services_restarted') + ] + sh = cfn_helper.ServicesHandler(services, 'resource1', hooks) + sh.apply_services() + # services not running + sh.monitor_services() + + # services running + sh.monitor_services() + + self.m.VerifyAll() + + def test_services_handler_sysv_disabled_service_chkconfig(self): + self.m.StubOutWithMock(os.path, 'exists') + os.path.exists('/bin/systemctl').MultipleTimes().AndReturn(False) + os.path.exists('/sbin/service').MultipleTimes().AndReturn(True) + os.path.exists('/sbin/chkconfig').MultipleTimes().AndReturn(True) + # apply_services + self.mock_cmd_run( + ['su', 'root', '-c', '/sbin/chkconfig httpd off'] + ).AndReturn(FakePOpen()) self.mock_cmd_run( ['su', 'root', '-c', '/sbin/service httpd status'] - ).AndReturn(FakePOpen(returncode=-1)) + ).AndReturn(FakePOpen()) self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service httpd start'] + ['su', 'root', '-c', '/sbin/service httpd stop'] ).AndReturn(FakePOpen()) + + self.m.ReplayAll() + + services = { + "sysvinit": { + "httpd": {"enabled": "false", "ensureRunning": "false"} + } + } + hooks = [ + cfn_helper.Hook( + 'hook1', + 'service.restarted', + 'Resources.resource1.Metadata', + 'root', + '/bin/services_restarted') + ] + sh = cfn_helper.ServicesHandler(services, 'resource1', hooks) + sh.apply_services() + + self.m.VerifyAll() + + def test_services_handler_sysv_systemctl(self): + self.m.StubOutWithMock(os.path, 'exists') + os.path.exists('/bin/systemctl').MultipleTimes().AndReturn(True) + # apply_services self.mock_cmd_run( - ['su', 'root', '-c', '/bin/services_restarted'] + ['su', 'root', '-c', '/bin/systemctl enable httpd.service'] ).AndReturn(FakePOpen()) self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service mysqld status'] + ['su', 'root', '-c', '/bin/systemctl status httpd.service'] ).AndReturn(FakePOpen(returncode=-1)) self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service mysqld start'] + ['su', 'root', '-c', '/bin/systemctl start httpd.service'] ).AndReturn(FakePOpen()) + + # monitor_services not running self.mock_cmd_run( - ['su', 'root', '-c', '/bin/services_restarted'] + ['su', 'root', '-c', '/bin/systemctl status httpd.service'] + ).AndReturn(FakePOpen(returncode=-1)) + self.mock_cmd_run( + ['su', 'root', '-c', '/bin/systemctl start httpd.service'] ).AndReturn(FakePOpen()) - - # monitor_services running self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service httpd status'] + ['su', 'root', '-c', '/bin/services_restarted'] ).AndReturn(FakePOpen()) + # monitor_services running self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service mysqld status'] + ['su', 'root', '-c', '/bin/systemctl status httpd.service'] ).AndReturn(FakePOpen()) self.m.ReplayAll() services = { "sysvinit": { - "mysqld": {"enabled": "true", "ensureRunning": "true"}, "httpd": {"enabled": "true", "ensureRunning": "true"} } } @@ -351,32 +427,117 @@ class TestServicesHandler(MockPopenTestCase): self.m.VerifyAll() - def test_services_handler_sysv_disabled(self): + def test_services_handler_sysv_disabled_systemctl(self): + self.m.StubOutWithMock(os.path, 'exists') + os.path.exists('/bin/systemctl').MultipleTimes().AndReturn(True) # apply_services self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/chkconfig httpd off'] + ['su', 'root', '-c', '/bin/systemctl disable httpd.service'] ).AndReturn(FakePOpen()) self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service httpd status'] + ['su', 'root', '-c', '/bin/systemctl status httpd.service'] ).AndReturn(FakePOpen()) self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service httpd stop'] + ['su', 'root', '-c', '/bin/systemctl stop httpd.service'] + ).AndReturn(FakePOpen()) + + self.m.ReplayAll() + + services = { + "sysvinit": { + "httpd": {"enabled": "false", "ensureRunning": "false"} + } + } + hooks = [ + cfn_helper.Hook( + 'hook1', + 'service.restarted', + 'Resources.resource1.Metadata', + 'root', + '/bin/services_restarted') + ] + sh = cfn_helper.ServicesHandler(services, 'resource1', hooks) + sh.apply_services() + + self.m.VerifyAll() + + def test_services_handler_sysv_service_updaterc(self): + self.m.StubOutWithMock(os.path, 'exists') + os.path.exists('/bin/systemctl').MultipleTimes().AndReturn(False) + os.path.exists('/sbin/service').MultipleTimes().AndReturn(False) + os.path.exists('/sbin/chkconfig').MultipleTimes().AndReturn(False) + # apply_services + self.mock_cmd_run( + ['su', 'root', '-c', '/usr/sbin/update-rc.d httpd enable'] + ).AndReturn(FakePOpen()) + self.mock_cmd_run( + ['su', 'root', '-c', '/usr/sbin/service httpd status'] + ).AndReturn(FakePOpen(returncode=-1)) + self.mock_cmd_run( + ['su', 'root', '-c', '/usr/sbin/service httpd start'] + ).AndReturn(FakePOpen()) + + # monitor_services not running + self.mock_cmd_run( + ['su', 'root', '-c', '/usr/sbin/service httpd status'] + ).AndReturn(FakePOpen(returncode=-1)) + self.mock_cmd_run( + ['su', 'root', '-c', '/usr/sbin/service httpd start'] + ).AndReturn(FakePOpen()) + self.mock_cmd_run( + ['su', 'root', '-c', '/bin/services_restarted'] + ).AndReturn(FakePOpen()) + + # monitor_services running + self.mock_cmd_run( + ['su', 'root', '-c', '/usr/sbin/service httpd status'] ).AndReturn(FakePOpen()) + + self.m.ReplayAll() + + services = { + "sysvinit": { + "httpd": {"enabled": "true", "ensureRunning": "true"} + } + } + hooks = [ + cfn_helper.Hook( + 'hook1', + 'service.restarted', + 'Resources.resource1.Metadata', + 'root', + '/bin/services_restarted') + ] + sh = cfn_helper.ServicesHandler(services, 'resource1', hooks) + sh.apply_services() + # services not running + sh.monitor_services() + + # services running + sh.monitor_services() + + self.m.VerifyAll() + + def test_services_handler_sysv_disabled_service_updaterc(self): + self.m.StubOutWithMock(os.path, 'exists') + os.path.exists('/bin/systemctl').MultipleTimes().AndReturn(False) + os.path.exists('/sbin/service').MultipleTimes().AndReturn(False) + os.path.exists('/sbin/chkconfig').MultipleTimes().AndReturn(False) + # apply_services self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/chkconfig mysqld off'] + ['su', 'root', '-c', '/usr/sbin/update-rc.d httpd disable'] ).AndReturn(FakePOpen()) self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service mysqld status'] + ['su', 'root', '-c', '/usr/sbin/service httpd status'] ).AndReturn(FakePOpen()) self.mock_cmd_run( - ['su', 'root', '-c', '/sbin/service mysqld stop'] + ['su', 'root', '-c', '/usr/sbin/service httpd stop'] ).AndReturn(FakePOpen()) self.m.ReplayAll() services = { "sysvinit": { - "mysqld": {"enabled": "false", "ensureRunning": "false"}, "httpd": {"enabled": "false", "ensureRunning": "false"} } } |