]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/cephadm/upgrade: implement N-2 version checks on upgrade start
authorSage Weil <sage@newdream.net>
Mon, 25 Jan 2021 20:00:04 +0000 (14:00 -0600)
committerSage Weil <sage@newdream.net>
Mon, 1 Feb 2021 22:23:02 +0000 (16:23 -0600)
- Prevent major upgrades that span > 2 releases
- Prevent downgrades to an rc or dev release

Signed-off-by: Sage Weil <sage@newdream.net>
src/pybind/mgr/cephadm/tests/test_upgrade.py
src/pybind/mgr/cephadm/upgrade.py

index baa6c50a1a961ee1b96dc82cbe37df4364af96d8..caf47aef97e516c348af99e4030c1b4ac3a28a31 100644 (file)
@@ -37,7 +37,18 @@ def test_upgrade_run(use_repo_digest, cephadm_module: CephadmOrchestrator):
         cephadm_module.set_container_image('global', 'from_image')
         if use_repo_digest:
             cephadm_module.use_repo_digest = True
-        with with_service(cephadm_module, ServiceSpec('mgr'), CephadmOrchestrator.apply_mgr, 'test'):
+        with with_service(cephadm_module, ServiceSpec('mgr'), CephadmOrchestrator.apply_mgr, 'test'),\
+             mock.patch("cephadm.module.CephadmOrchestrator.lookup_release_name",
+                        return_value='foo'),\
+             mock.patch("cephadm.module.CephadmOrchestrator.version",
+                        new_callable=mock.PropertyMock) as version_mock,\
+             mock.patch("cephadm.module.CephadmOrchestrator.get",
+                        return_value={
+                            # capture fields in both mon and osd maps
+                            "require_osd_release": "pacific",
+                            "min_mon_release": 16,
+                        }):
+            version_mock.return_value = 'ceph version 18.2.1 (somehash)'
             assert wait(cephadm_module, cephadm_module.upgrade_start(
                 'to_image', None)) == 'Initiating upgrade to to_image'
 
@@ -55,6 +66,7 @@ def test_upgrade_run(use_repo_digest, cephadm_module: CephadmOrchestrator):
             with mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm(json.dumps({
                 'image_id': 'image_id',
                 'repo_digest': 'to_image@repo_digest',
+                'ceph_version': 'ceph version 18.2.3 (hash)',
             }))):
 
                 cephadm_module.upgrade._do_upgrade()
index 4e21a99547a13310ff42380674269f701b9886f2..d3f4f165677711f29de29362fd37447ccf95a92c 100644 (file)
@@ -90,19 +90,50 @@ class CephadmUpgrade:
                 r.message = 'Upgrade paused'
         return r
 
+    def _check_target_version(self, version: str) -> Optional[str]:
+        try:
+            (major, minor, patch) = version.split('.')
+            assert int(minor) >= 0
+            assert int(patch) >= 0
+        except:
+            return 'version must be in the form X.Y.Z (e.g., 15.2.3)'
+        if int(major) < 15 or (int(major) == 15 and int(minor) < 2):
+            return 'cephadm only supports octopus (15.2.0) or later'
+
+        # to far a jump?
+        current_version = self.mgr.version.split('ceph version ')[1]
+        current_major, current_minor, current_patch = current_version.split('-')[0].split('.')
+        if int(current_major) < int(major) - 2:
+            return f'ceph can only upgrade 1 or 2 major versions at a time; {current_version} -> {version} is too big a jump'
+        if int(current_major) > int(major):
+            return f'ceph cannot downgrade major versions (from {current_version} to {version})'
+        if int(current_major) == int(major):
+            if int(current_minor) > int(minor):
+                return f'ceph cannot downgrade to a {"rc" if minor == "1" else "dev"} release'
+
+        # check mon min
+        monmap = self.mgr.get("mon_map")
+        mon_min = monmap.get("min_mon_release", 0)
+        if mon_min < int(major) - 2:
+            return f'min_mon_release ({mon_min}) < target {major} - 2; first complete an upgrade to an earlier release'
+
+        # check osd min
+        osdmap = self.mgr.get("osd_map")
+        osd_min_name = osdmap.get("require_osd_release", "argonaut")
+        osd_min = ord(osd_min_name[0]) - ord('a') + 1
+        if osd_min < int(major) - 2:
+            return f'require_osd_release ({osd_min_name} or {osd_min}) < target {major} - 2; first complete an upgrade to an earlier release'
+
+        return None
+
     def upgrade_start(self, image: str, version: str) -> str:
         if self.mgr.mode != 'root':
             raise OrchestratorError('upgrade is not supported in %s mode' % (
                 self.mgr.mode))
         if version:
-            try:
-                (major, minor, patch) = version.split('.')
-                assert int(minor) >= 0
-                assert int(patch) >= 0
-            except:
-                raise OrchestratorError('version must be in the form X.Y.Z (e.g., 15.2.3)')
-            if int(major) < 15 or (int(major) == 15 and int(minor) < 2):
-                raise OrchestratorError('cephadm only supports octopus (15.2.0) or later')
+            version_error = self._check_target_version(version)
+            if version_error:
+                raise OrchestratorError(version_error)
             target_name = self.mgr.container_image_base + ':v' + version
         elif image:
             target_name = image
@@ -269,8 +300,9 @@ class CephadmUpgrade:
             self._save_upgrade_state()
             target_image = self.target_image
         target_version = self.upgrade_state.target_version
-        logger.info('Upgrade: Target is %s with id %s' % (target_image,
-                                                          target_id))
+        target_major, target_minor, target_patch = target_version.split('.')
+        logger.info('Upgrade: Target is version %s, container %s with id %s' % (
+            target_version, target_image, target_id))
 
         image_settings = self.get_distinct_container_image_settings()