1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
# -*- coding: utf-8 -*-
import json
import logging
from orchestrator import OrchestratorError
from .. import mgr
from ..exceptions import DashboardException
from ..services.orchestrator import OrchClient
logger = logging.getLogger('nvmeof_conf')
class NvmeofGatewayAlreadyExists(Exception):
def __init__(self, gateway_name):
super(NvmeofGatewayAlreadyExists, self).__init__(
"NVMe-oF gateway '{}' already exists".format(gateway_name))
class NvmeofGatewayDoesNotExist(Exception):
def __init__(self, hostname):
super(NvmeofGatewayDoesNotExist, self).__init__(
"NVMe-oF gateway '{}' does not exist".format(hostname))
class ManagedByOrchestratorException(Exception):
def __init__(self):
super(ManagedByOrchestratorException, self).__init__(
"NVMe-oF configuration is managed by the orchestrator")
_NVMEOF_STORE_KEY = "_nvmeof_config"
class NvmeofGatewaysConfig(object):
@classmethod
def _load_config_from_store(cls):
json_db = mgr.get_store(_NVMEOF_STORE_KEY,
'{"gateways": {}}')
config = json.loads(json_db)
cls._save_config(config)
return config
@classmethod
def _save_config(cls, config):
mgr.set_store(_NVMEOF_STORE_KEY, json.dumps(config))
@classmethod
def get_gateways_config(cls):
return cls._load_config_from_store()
@classmethod
def add_gateway(cls, name, service_url, group, daemon_name):
config = cls.get_gateways_config()
if name in config.get('gateways', {}):
existing_gateways = config['gateways'][name]
for gateway in existing_gateways:
if 'daemon_name' not in gateway:
gateway['daemon_name'] = daemon_name
break
if gateway['service_url'] == service_url:
return
new_gateway = {
'service_url': service_url,
'group': group,
'daemon_name': daemon_name
}
if name in config.get('gateways', {}):
config['gateways'][name].append(new_gateway)
else:
config['gateways'][name] = [new_gateway]
cls._save_config(config)
@classmethod
def remove_gateway(cls, name, daemon_name=None):
config = cls.get_gateways_config()
if name not in config['gateways']:
raise NvmeofGatewayDoesNotExist(name)
if not daemon_name:
del config['gateways'][name]
else:
# remove the daemon from the list of gateways
config['gateways'][name] = [daemon for daemon in config['gateways'][name]
if daemon['daemon_name'] != daemon_name]
# if there are no more daemons in the list, remove the gateway
if not config['gateways'][name]:
del config['gateways'][name]
cls._save_config(config)
@classmethod
def get_service_info(cls, group=None):
try:
config = cls.get_gateways_config()
gateways = config.get('gateways', {})
if not gateways:
return None
if group:
return _get_name_url_for_group(gateways, group)
return _get_default_service(gateways)
except (KeyError, IndexError) as e:
raise DashboardException(
msg=f'NVMe-oF configuration is not set: {e}',
)
@classmethod
def get_client_cert(cls, service_name: str):
client_cert = cls.from_cert_store('nvmeof_client_cert', service_name)
return client_cert.encode() if client_cert else None
@classmethod
def get_client_key(cls, service_name: str):
client_key = cls.from_cert_store('nvmeof_client_key', service_name, key=True)
return client_key.encode() if client_key else None
@classmethod
def get_root_ca_cert(cls, service_name: str):
root_ca_cert = cls.from_cert_store('nvmeof_root_ca_cert', service_name)
# If root_ca_cert is not set, use server_cert as root_ca_cert
return root_ca_cert.encode() if root_ca_cert else cls.get_server_cert(service_name)
@classmethod
def get_server_cert(cls, service_name: str):
server_cert = cls.from_cert_store('nvmeof_server_cert', service_name)
return server_cert.encode() if server_cert else None
@classmethod
def from_cert_store(cls, entity: str, service_name: str, key=False):
try:
orch = OrchClient.instance()
if orch.available():
if key:
return orch.cert_store.get_key(entity, service_name,
ignore_missing_exception=True)
return orch.cert_store.get_cert(entity, service_name,
ignore_missing_exception=True)
return None
except OrchestratorError:
# just return None if any orchestrator error is raised
# otherwise nvmeof api will raise this error and doesn't proceed.
return None
def _get_name_url_for_group(gateways, group):
try:
orch = OrchClient.instance()
for service_name, svc_config in gateways.items():
# get the group name of the service and match it against the
# group name provided
group_name_from_svc = orch.services.get(service_name)[0].spec.group
if group == group_name_from_svc:
running_daemons = _get_running_daemons(orch, service_name)
config = _get_running_daemon_svc_config(svc_config, running_daemons)
if config:
return service_name, config['service_url']
return None
except OrchestratorError:
return _get_default_service(gateways)
def _get_running_daemons(orch, service_name):
# get the running nvmeof daemons
daemons = [d.to_dict()
for d in orch.services.list_daemons(service_name=service_name)]
return [d['daemon_name'] for d in daemons
if d['status_desc'] == 'running']
def _get_running_daemon_svc_config(svc_config, running_daemons):
try:
return next(config for config in svc_config
if config['daemon_name'] in running_daemons)
except StopIteration:
return None
def _get_default_service(gateways):
if gateways:
gateway_keys = list(gateways.keys())
# if there are more than 1 gateway, rather than chosing a random gateway
# from any of the group, raise an exception to make it clear that we need
# to specify the group name in the API request.
if len(gateway_keys) > 1:
raise DashboardException(
msg=(
"Multiple NVMe-oF gateway groups are configured. "
"Please specify the 'gw_group' parameter in the request."
),
component="nvmeof"
)
service_name = gateway_keys[0]
return service_name, gateways[service_name][0]['service_url']
return None
|