diff --git a/octoprint_mrbeam/files/migrate_logrotate/analytics b/octoprint_mrbeam/files/migrate_logrotate/analytics index 31d5a5934..c0728c667 100644 --- a/octoprint_mrbeam/files/migrate_logrotate/analytics +++ b/octoprint_mrbeam/files/migrate_logrotate/analytics @@ -3,9 +3,11 @@ /home/pi/.octoprint/analytics/analytics_log.json { size 100M - rotate 2 + rotate 3 compress delaycompress missingok notifempty + dateext + dateformat .%Y-%m-%d } diff --git a/octoprint_mrbeam/files/migrate_logrotate/iobeam b/octoprint_mrbeam/files/migrate_logrotate/iobeam index b6e4f8898..d02feb87c 100644 --- a/octoprint_mrbeam/files/migrate_logrotate/iobeam +++ b/octoprint_mrbeam/files/migrate_logrotate/iobeam @@ -1,9 +1,11 @@ /var/log/iobeam.log { size 2M - rotate 4 + rotate 3 compress delaycompress missingok notifempty copytruncate + dateext + dateformat .%Y-%m-%d } diff --git a/octoprint_mrbeam/files/migrate_logrotate/mount_manager b/octoprint_mrbeam/files/migrate_logrotate/mount_manager index b29361734..342119ce2 100644 --- a/octoprint_mrbeam/files/migrate_logrotate/mount_manager +++ b/octoprint_mrbeam/files/migrate_logrotate/mount_manager @@ -1,10 +1,12 @@ /var/log/mount_manager.log { size 2M - rotate 2 + rotate 3 compress delaycompress missingok notifempty copytruncate create 644 root root + dateext + dateformat .%Y-%m-%d } diff --git a/octoprint_mrbeam/files/migrate_logrotate/mrb_check b/octoprint_mrbeam/files/migrate_logrotate/mrb_check index ed448832f..19798fb3c 100644 --- a/octoprint_mrbeam/files/migrate_logrotate/mrb_check +++ b/octoprint_mrbeam/files/migrate_logrotate/mrb_check @@ -1,10 +1,12 @@ /var/log/mrb_check.log { size 5M - rotate 4 + rotate 3 compress delaycompress missingok notifempty copytruncate create 644 root root + dateext + dateformat .%Y-%m-%d } diff --git a/octoprint_mrbeam/files/migrate_logrotate/mrbeam_ledstrips b/octoprint_mrbeam/files/migrate_logrotate/mrbeam_ledstrips index 816b2ac85..24c987fb0 100644 --- a/octoprint_mrbeam/files/migrate_logrotate/mrbeam_ledstrips +++ b/octoprint_mrbeam/files/migrate_logrotate/mrbeam_ledstrips @@ -1,10 +1,12 @@ /var/log/mrbeam_ledstrips.log { size 2M - rotate 2 + rotate 3 compress delaycompress missingok notifempty copytruncate create 644 root root + dateext + dateformat .%Y-%m-%d } diff --git a/octoprint_mrbeam/files/migrate_logrotate/netconnectd b/octoprint_mrbeam/files/migrate_logrotate/netconnectd index 4cd6caf3a..8fd9067b7 100644 --- a/octoprint_mrbeam/files/migrate_logrotate/netconnectd +++ b/octoprint_mrbeam/files/migrate_logrotate/netconnectd @@ -1,9 +1,11 @@ /var/log/netconnectd.log { size 2M - rotate 2 + rotate 3 compress delaycompress missingok notifempty copytruncate + dateext + dateformat .%Y-%m-%d } diff --git a/octoprint_mrbeam/migration/Mig003.py b/octoprint_mrbeam/migration/Mig003.py new file mode 100644 index 000000000..b6ac2b3e9 --- /dev/null +++ b/octoprint_mrbeam/migration/Mig003.py @@ -0,0 +1,54 @@ +import os + +from octoprint_mrbeam.migration.migration_base import ( + MigrationBaseClass, +) + + +class Mig003EnableLogrotateBuster(MigrationBaseClass): + """ + This migration should add logrotate for the buster image and change the lorotate for the legacy image + """ + + MIGRATE_LOGROTATE_FOLDER = "files/migrate_logrotate/" + BEAMOS_VERSION_LOW = "0.14.0" + BEAMOS_VERSION_HIGH = "0.18.2" + + def __init__(self, plugin): + super(Mig003EnableLogrotateBuster, self).__init__(plugin) + + @property + def id(self): + return "003" + + def _run(self): + self._logger.debug("delete wrong iobeam logrotate") + self.exec_cmd("sudo rm /etc/logrotate.d/iobeam.logrotate", optional=True) + + logrotates = [ + "analytics", + "iobeam", + "mount_manager", + "mrb_check", + "mrbeam_ledstrips", + "netconnectd", + ] + for logrotate in logrotates: + self._logger.debug("enable logrotate of " + logrotate) + src = os.path.join( + __package_path__, self.MIGRATE_LOGROTATE_FOLDER, logrotate + ) + dst = os.path.join("/etc/logrotate.d/" + logrotate) + self.exec_cmd("sudo cp {src} {dst}".format(src=src, dst=dst)) + + self._logger.debug( + "restarting logrotate in order for the changed config to take effect" + ) + + # needs to be optional for legacy image, as this is returning 1 instead of 0 + self.exec_cmd("sudo logrotate /etc/logrotate.conf", optional=True) + super(Mig003EnableLogrotateBuster, self)._run() + + def _rollback(self): + # no rollback needed + super(Mig003EnableLogrotateBuster, self)._rollback() diff --git a/octoprint_mrbeam/migration/__init__.py b/octoprint_mrbeam/migration/__init__.py index 4f28b8484..cc199db61 100644 --- a/octoprint_mrbeam/migration/__init__.py +++ b/octoprint_mrbeam/migration/__init__.py @@ -23,9 +23,11 @@ # this is for internal use from octoprint_mrbeam.migration.Mig001 import Mig001NetconnectdDisableLogDebugLevel from octoprint_mrbeam.migration.Mig002 import Mig002EnableOnlineCheck +from octoprint_mrbeam.migration.Mig003 import Mig003EnableLogrotateBuster # To add migrations they have to be added to this list till we automate it list_of_migrations = [ Mig001NetconnectdDisableLogDebugLevel, Mig002EnableOnlineCheck, + Mig003EnableLogrotateBuster, ] diff --git a/octoprint_mrbeam/migration/migration_base.py b/octoprint_mrbeam/migration/migration_base.py index c86fd5c87..15882aa38 100644 --- a/octoprint_mrbeam/migration/migration_base.py +++ b/octoprint_mrbeam/migration/migration_base.py @@ -201,7 +201,7 @@ def state(self): """ return self._state - def exec_cmd(self, command): + def exec_cmd(self, command, optional=False): """ wrapper of exec_cmd to change to errorstate in case of a error Args: @@ -210,10 +210,19 @@ def exec_cmd(self, command): Returns: None Raises: - MigrationException: if the execution of the command ended with a errorcode different to 0 + MigrationException: if the execution of the command was not successful """ - if not exec_cmd(command): - raise MigrationException("error during migration for cmd:", command) + command_success = exec_cmd(command) + if command_success: + return + if optional and not command_success: + self._logger.warn("optional command failed - cmd: {}".format(command)) + else: + raise MigrationException( + "error during migration for cmd: {} - return: {}".format( + command, command_success + ) + ) @staticmethod def execute_restart(restart): diff --git a/octoprint_mrbeam/services/document_service.py b/octoprint_mrbeam/services/document_service.py index 60b725b18..90d646ace 100644 --- a/octoprint_mrbeam/services/document_service.py +++ b/octoprint_mrbeam/services/document_service.py @@ -47,7 +47,10 @@ def get_document_simple_for(self, definition, language): def _get_title_translated(self, definition): title_key = string_util.separate_camelcase_words(definition.mrbeamdoc_type.value) title_translated = gettext(title_key) - self._log_error_on_missing_translation(title_key, str(title_translated)) + if title_translated: + self._log_error_on_missing_translation(title_key, title_translated.encode('utf-8', 'ignore')) + else: + self._logger.error('title_translated is None. This should never be the case. Please check me.') return title_translated @staticmethod diff --git a/octoprint_mrbeam/static/js/messages.js b/octoprint_mrbeam/static/js/messages.js index 584031ee8..da49ab124 100644 --- a/octoprint_mrbeam/static/js/messages.js +++ b/octoprint_mrbeam/static/js/messages.js @@ -241,7 +241,12 @@ $(function () { var lmid = -1; self.messages().forEach(function (myMessage) { lmid = Math.max(lmid, myMessage.id); - if (myMessage.notification && myMessage.id > self.lastMessageId) { + if (myMessage.notification && (myMessage.id > self.lastMessageId + // The below condition is only added to keep the 0.11.0 2-step update message notification showing + // if the user stayed on the 0.11.0rc1 version and didn't execute the 2nd software update + // Message ID '3' should always be assigned to this specific 2-step update message + // TODO: SW-1845: Should be removed after 0.11.0 stable release + || (myMessage.id === 3 && MRBEAM_PLUGIN_VERSION === "0.11.0rc1"))) { try { new PNotify({ title: myMessage.notification.title || '', diff --git a/octoprint_mrbeam/util/cmd_exec.py b/octoprint_mrbeam/util/cmd_exec.py index 7741ba72d..c47edfcf9 100644 --- a/octoprint_mrbeam/util/cmd_exec.py +++ b/octoprint_mrbeam/util/cmd_exec.py @@ -19,7 +19,7 @@ def exec_cmd(cmd, log=True, shell=True, loglvl=DEBUG): try: code = subprocess.call(cmd, shell=shell) except Exception as e: - _logger.debug( + _logger.error( "Failed to execute command '%s', return code: %s, Exception: %s", cmd, code, @@ -27,7 +27,7 @@ def exec_cmd(cmd, log=True, shell=True, loglvl=DEBUG): ) return None if code != 0 and log: - _logger.info("cmd= '%s', return code: '%s'", code) + _logger.error("cmd= '{}', return code: {}".format(cmd, code)) return code == 0 diff --git a/tests/migrations/test-migration-Mig003.py b/tests/migrations/test-migration-Mig003.py new file mode 100644 index 000000000..d3ca7b8e2 --- /dev/null +++ b/tests/migrations/test-migration-Mig003.py @@ -0,0 +1,63 @@ +import os + +from mock import patch + +from octoprint_mrbeam.migration import Mig003EnableLogrotateBuster +import unittest + + +class TestMigrationMig003(unittest.TestCase): + """ + Testclass for the migration Mig001 + """ + + def setUp(self): + self.m003 = Mig003EnableLogrotateBuster(None) + + def test_beamos_versions(self): + # beamos versions where the migration should not run + self.assertFalse(self.m003.shouldrun(Mig003EnableLogrotateBuster, "0.19.0")) + + # beamos versions where the migration should run + self.assertTrue(self.m003.shouldrun(Mig003EnableLogrotateBuster, "0.14.0")) + self.assertTrue(self.m003.shouldrun(Mig003EnableLogrotateBuster, "0.18.0")) + self.assertTrue(self.m003.shouldrun(Mig003EnableLogrotateBuster, "0.18.1")) + self.assertTrue(self.m003.shouldrun(Mig003EnableLogrotateBuster, "0.18.2")) + + # not matching pattern strings + self.assertFalse(self.m003.shouldrun(Mig003EnableLogrotateBuster, None)) + self.assertFalse(self.m003.shouldrun(Mig003EnableLogrotateBuster, "14.0")) + self.assertFalse(self.m003.shouldrun(Mig003EnableLogrotateBuster, "0")) + + def test_migration_id(self): + self.assertEqual(self.m003.id, "003") + + @patch.object( + Mig003EnableLogrotateBuster, + "exec_cmd", + ) + def test_commands_executed(self, exec_cmd_mock): + self.m003.run() + exec_cmd_mock.assert_any_call( + "sudo rm /etc/logrotate.d/iobeam.logrotate", optional=True + ) + logrotates = [ + "analytics", + "iobeam", + "mount_manager", + "mrb_check", + "mrbeam_ledstrips", + "netconnectd", + ] + for logrotate in logrotates: + dst = os.path.join("/etc/logrotate.d/" + logrotate) + src = os.path.join( + __package_path__, + "files/migrate_logrotate", + logrotate, + ) + exec_cmd_mock.assert_any_call( + "sudo cp {src} {dst}".format(src=src, dst=dst) + ) + + exec_cmd_mock.ssert_any_call("sudo logrotate /etc/logrotate.conf") diff --git a/tests/services/test_burger_menu_service.py b/tests/services/test_burger_menu_service.py index 3551d5306..6c8698926 100644 --- a/tests/services/test_burger_menu_service.py +++ b/tests/services/test_burger_menu_service.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from unittest import TestCase from mock import patch, MagicMock @@ -52,3 +53,36 @@ def test_get_burger_menu_model__with_supported_language__should_return_documents self.assertIsNot(len(burger_menu_model.documents), 0) for document in burger_menu_model.documents: self.assertEquals(document.document_link.language, SupportedLanguage.GERMAN) + + @patch('octoprint_mrbeam.services.burger_menu_service.MrBeamDocUtils.get_mrbeam_definitions_for') + @patch('octoprint_mrbeam.services.document_service.gettext') + @patch('octoprint_mrbeam.services.burger_menu_service.get_locale') + def test_get_burger_menu_model__with_unicode_translation__should_work(self, get_locale_mock, get_text_mock, + get_mrbeam_definitions_for_mock): + get_locale_mock.return_value = MagicMock(language='es') + get_text_mock.return_value = u'Guía de usuário äëïöü l\'ecole très Garçon sûr' + MOCK_DEFINITION = MrBeamDocDefinition(MrBeamDocType.USER_MANUAL, MrBeamModel.MRBEAM2, + [SupportedLanguage.ENGLISH, SupportedLanguage.SPANISH]) + get_mrbeam_definitions_for_mock.return_value = [MOCK_DEFINITION] + burger_menu_model = self._burger_menu_service.get_burger_menu_model(MrBeamModel.MRBEAM2.value) + self.assertIsNot(len(burger_menu_model.documents), 0) + for document in burger_menu_model.documents: + self.assertEquals(document.document_link.language, SupportedLanguage.SPANISH) + + @patch('octoprint_mrbeam.services.burger_menu_service.MrBeamDocUtils.get_mrbeam_definitions_for') + @patch('octoprint_mrbeam.services.document_service.gettext') + @patch('octoprint_mrbeam.services.burger_menu_service.get_locale') + def test_get_burger_menu_model__with_null_translation__should_not_crash_and_return_title_as_None(self, + get_locale_mock, + get_text_mock, + get_mrbeam_definitions_for_mock): + get_locale_mock.return_value = MagicMock(language='es') + get_text_mock.return_value = None + MOCK_DEFINITION = MrBeamDocDefinition(MrBeamDocType.USER_MANUAL, MrBeamModel.MRBEAM2, + [SupportedLanguage.ENGLISH, SupportedLanguage.SPANISH]) + get_mrbeam_definitions_for_mock.return_value = [MOCK_DEFINITION] + burger_menu_model = self._burger_menu_service.get_burger_menu_model(MrBeamModel.MRBEAM2.value) + self.assertIsNot(len(burger_menu_model.documents), 0) + for document in burger_menu_model.documents: + self.assertEquals(document.document_link.language, SupportedLanguage.SPANISH) + self.assertEquals(document.title, None)