]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/services/iscsi_config.py
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / pybind / mgr / dashboard / services / iscsi_config.py
1 # -*- coding: utf-8 -*-
2 from __future__ import absolute_import
3
4 import json
5
6 from orchestrator import OrchestratorError
7
8 try:
9 from urlparse import urlparse
10 except ImportError:
11 from urllib.parse import urlparse
12
13 from mgr_util import merge_dicts
14 from .orchestrator import OrchClient
15 from .. import mgr
16
17
18 class IscsiGatewayAlreadyExists(Exception):
19 def __init__(self, gateway_name):
20 super(IscsiGatewayAlreadyExists, self).__init__(
21 "iSCSI gateway '{}' already exists".format(gateway_name))
22
23
24 class IscsiGatewayDoesNotExist(Exception):
25 def __init__(self, hostname):
26 super(IscsiGatewayDoesNotExist, self).__init__(
27 "iSCSI gateway '{}' does not exist".format(hostname))
28
29
30 class InvalidServiceUrl(Exception):
31 def __init__(self, service_url):
32 super(InvalidServiceUrl, self).__init__(
33 "Invalid service URL '{}'. "
34 "Valid format: '<scheme>://<username>:<password>@<host>[:port]'.".format(service_url))
35
36
37 class ManagedByOrchestratorException(Exception):
38 def __init__(self):
39 super(ManagedByOrchestratorException, self).__init__(
40 "iSCSI configuration is managed by the orchestrator")
41
42
43 _ISCSI_STORE_KEY = "_iscsi_config"
44
45
46 class IscsiGatewaysConfig(object):
47 @classmethod
48 def _load_config_from_store(cls):
49 json_db = mgr.get_store(_ISCSI_STORE_KEY,
50 '{"gateways": {}}')
51 config = json.loads(json_db)
52 cls.update_iscsi_config(config)
53 return config
54
55 @classmethod
56 def update_iscsi_config(cls, config):
57 """
58 Since `ceph-iscsi` config v10, gateway names were renamed from host short name to FQDN.
59 If Ceph Dashboard were configured before v10, we try to update our internal gateways
60 database automatically.
61 """
62 for gateway_name, gateway_config in config['gateways'].items():
63 if '.' not in gateway_name:
64 from .iscsi_client import IscsiClient
65 from ..rest_client import RequestException
66 try:
67 service_url = gateway_config['service_url']
68 new_gateway_name = IscsiClient.instance(
69 service_url=service_url).get_hostname()['data']
70 if gateway_name != new_gateway_name:
71 config['gateways'][new_gateway_name] = gateway_config
72 del config['gateways'][gateway_name]
73 cls._save_config(config)
74 except RequestException:
75 # If gateway is not acessible, it should be removed manually
76 # or we will try to update automatically next time
77 continue
78
79 @staticmethod
80 def _load_config_from_orchestrator():
81 config = {'gateways': {}}
82 try:
83 instances = OrchClient().list_service_info("iscsi")
84 for instance in instances:
85 config['gateways'][instance.nodename] = {
86 'service_url': instance.service_url
87 }
88 except (RuntimeError, OrchestratorError, ImportError):
89 pass
90 return config
91
92 @classmethod
93 def _save_config(cls, config):
94 mgr.set_store(_ISCSI_STORE_KEY, json.dumps(config))
95
96 @classmethod
97 def validate_service_url(cls, service_url):
98 url = urlparse(service_url)
99 if not url.scheme or not url.hostname or not url.username or not url.password:
100 raise InvalidServiceUrl(service_url)
101
102 @classmethod
103 def add_gateway(cls, name, service_url):
104 config = cls.get_gateways_config()
105 if name in config:
106 raise IscsiGatewayAlreadyExists(name)
107 IscsiGatewaysConfig.validate_service_url(service_url)
108 config['gateways'][name] = {'service_url': service_url}
109 cls._save_config(config)
110
111 @classmethod
112 def remove_gateway(cls, name):
113 if name in cls._load_config_from_orchestrator()['gateways']:
114 raise ManagedByOrchestratorException()
115
116 config = cls._load_config_from_store()
117 if name not in config['gateways']:
118 raise IscsiGatewayDoesNotExist(name)
119
120 del config['gateways'][name]
121 cls._save_config(config)
122
123 @classmethod
124 def get_gateways_config(cls):
125 orch_config = cls._load_config_from_orchestrator()
126 local_config = cls._load_config_from_store()
127
128 return {'gateways': merge_dicts(orch_config['gateways'], local_config['gateways'])}
129
130 @classmethod
131 def get_gateway_config(cls, name):
132 config = IscsiGatewaysConfig.get_gateways_config()
133 if name not in config['gateways']:
134 raise IscsiGatewayDoesNotExist(name)
135 return config['gateways'][name]