]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/pybind/mgr/dashboard/controllers/osd.py
import ceph quincy 17.2.4
[ceph.git] / ceph / src / pybind / mgr / dashboard / controllers / osd.py
index ceebb5acdafbfde4129831fdbe34ee64deddff6d..9c903ed672eec997627e79952fcbe169cb8f0634 100644 (file)
@@ -14,10 +14,11 @@ from ..security import Scope
 from ..services.ceph_service import CephService, SendCommandError
 from ..services.exception import handle_orchestrator_error, handle_send_command_error
 from ..services.orchestrator import OrchClient, OrchFeature
+from ..services.osd import HostStorageSummary, OsdDeploymentOptions
 from ..tools import str_to_bool
 from . import APIDoc, APIRouter, CreatePermission, DeletePermission, Endpoint, \
-    EndpointDoc, ReadPermission, RESTController, Task, UpdatePermission, \
-    allow_empty_body
+    EndpointDoc, ReadPermission, RESTController, Task, UIRouter, \
+    UpdatePermission, allow_empty_body
 from ._version import APIVersion
 from .orchestrator import raise_if_no_orchestrator
 
@@ -47,6 +48,72 @@ EXPORT_INDIV_FLAGS_GET_SCHEMA = {
 }
 
 
+class DeploymentOptions:
+    def __init__(self):
+        self.options = {
+            OsdDeploymentOptions.COST_CAPACITY:
+                HostStorageSummary(OsdDeploymentOptions.COST_CAPACITY,
+                                   title='Cost/Capacity-optimized',
+                                   desc='All the available HDDs are selected'),
+            OsdDeploymentOptions.THROUGHPUT:
+                HostStorageSummary(OsdDeploymentOptions.THROUGHPUT,
+                                   title='Throughput-optimized',
+                                   desc="HDDs/SSDs are selected for data"
+                                   "devices and SSDs/NVMes for DB/WAL devices"),
+            OsdDeploymentOptions.IOPS:
+                HostStorageSummary(OsdDeploymentOptions.IOPS,
+                                   title='IOPS-optimized',
+                                   desc='All the available NVMes are selected'),
+        }
+        self.recommended_option = None
+
+    def as_dict(self):
+        return {
+            'options': {k: v.as_dict() for k, v in self.options.items()},
+            'recommended_option': self.recommended_option
+        }
+
+
+predefined_drive_groups = {
+    OsdDeploymentOptions.COST_CAPACITY: {
+        'service_type': 'osd',
+        'service_id': 'cost_capacity',
+        'placement': {
+            'host_pattern': '*'
+        },
+        'data_devices': {
+            'rotational': 1
+        },
+        'encrypted': False
+    },
+    OsdDeploymentOptions.THROUGHPUT: {
+        'service_type': 'osd',
+        'service_id': 'throughput_optimized',
+        'placement': {
+            'host_pattern': '*'
+        },
+        'data_devices': {
+            'rotational': 1
+        },
+        'db_devices': {
+            'rotational': 0
+        },
+        'encrypted': False
+    },
+    OsdDeploymentOptions.IOPS: {
+        'service_type': 'osd',
+        'service_id': 'iops_optimized',
+        'placement': {
+            'host_pattern': '*'
+        },
+        'data_devices': {
+            'rotational': 0
+        },
+        'encrypted': False
+    },
+}
+
+
 def osd_task(name, metadata, wait_for=2.0):
     return Task("osd/{}".format(name), metadata, wait_for)
 
@@ -291,6 +358,18 @@ class Osd(RESTController):
             id=int(svc_id),
             weight=float(weight))
 
+    def _create_predefined_drive_group(self, data):
+        orch = OrchClient.instance()
+        option = OsdDeploymentOptions(data[0]['option'])
+        if option in list(OsdDeploymentOptions):
+            try:
+                predefined_drive_groups[
+                    option]['encrypted'] = data[0]['encrypted']
+                orch.osds.create([DriveGroupSpec.from_json(
+                    predefined_drive_groups[option])])
+            except (ValueError, TypeError, DriveGroupValidationError) as e:
+                raise DashboardException(e, component='osd')
+
     def _create_bare(self, data):
         """Create a OSD container that has no associated device.
 
@@ -330,6 +409,8 @@ class Osd(RESTController):
             return self._create_bare(data)
         if method == 'drive_groups':
             return self._create_with_drive_groups(data)
+        if method == 'predefined':
+            return self._create_predefined_drive_group(data)
         raise DashboardException(
             component='osd', http_status_code=400, msg='Unknown method: {}'.format(method))
 
@@ -405,6 +486,44 @@ class Osd(RESTController):
         return CephService.send_command('mon', 'device ls-by-daemon', who='osd.{}'.format(svc_id))
 
 
+@UIRouter('/osd', Scope.OSD)
+@APIDoc("Dashboard UI helper function; not part of the public API", "OsdUI")
+class OsdUi(Osd):
+    @Endpoint('GET')
+    @ReadPermission
+    @raise_if_no_orchestrator([OrchFeature.DAEMON_LIST])
+    @handle_orchestrator_error('host')
+    def deployment_options(self):
+        orch = OrchClient.instance()
+        hdds = 0
+        ssds = 0
+        nvmes = 0
+        res = DeploymentOptions()
+
+        for inventory_host in orch.inventory.list(hosts=None, refresh=True):
+            for device in inventory_host.devices.devices:
+                if device.available:
+                    if device.human_readable_type == 'hdd':
+                        hdds += 1
+                    # SSDs and NVMe are both counted as 'ssd'
+                    # so differentiating nvme using its path
+                    elif '/dev/nvme' in device.path:
+                        nvmes += 1
+                    else:
+                        ssds += 1
+
+        if hdds:
+            res.options[OsdDeploymentOptions.COST_CAPACITY].available = True
+            res.recommended_option = OsdDeploymentOptions.COST_CAPACITY
+        if hdds and ssds:
+            res.options[OsdDeploymentOptions.THROUGHPUT].available = True
+            res.recommended_option = OsdDeploymentOptions.THROUGHPUT
+        if nvmes:
+            res.options[OsdDeploymentOptions.IOPS].available = True
+
+        return res.as_dict()
+
+
 @APIRouter('/osd/flags', Scope.OSD)
 @APIDoc(group='OSD')
 class OsdFlagsController(RESTController):