Skip to content

Commit 0e3c55c

Browse files
authored
Merge pull request ceph#56190 from rhcs-dashboard/disable-multi-cluster-non-hub
mgr/dashboard: disable multi-cluster feature for non-hub clusters Reviewed-by: Ankush Behl <[email protected]>
2 parents e2d3bf8 + bd21182 commit 0e3c55c

15 files changed

+503
-509
lines changed

src/pybind/mgr/dashboard/controllers/multi_cluster.py

Lines changed: 89 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -56,40 +56,93 @@ def _proxy(self, method, base_url, path, params=None, payload=None, verify=False
5656
def auth(self, url: str, cluster_alias: str, username: str,
5757
password=None, token=None, hub_url=None, cluster_fsid=None,
5858
prometheus_api_url=None, ssl_verify=False, ssl_certificate=None):
59+
try:
60+
hub_fsid = mgr.get('config')['fsid']
61+
except KeyError:
62+
hub_fsid = ''
63+
5964
if password:
6065
payload = {
6166
'username': username,
6267
'password': password
6368
}
64-
content = self._proxy('POST', url, 'api/auth', payload=payload)
65-
if 'token' not in content:
66-
raise DashboardException(
67-
"Could not authenticate to remote cluster",
68-
http_status_code=400,
69-
component='dashboard')
70-
71-
cluster_token = content['token']
69+
cluster_token = self.check_cluster_connection(url, payload, username,
70+
ssl_verify, ssl_certificate)
7271

7372
self._proxy('PUT', url, 'ui-api/multi-cluster/set_cors_endpoint',
7473
payload={'url': hub_url}, token=cluster_token, verify=ssl_verify,
7574
cert=ssl_certificate)
75+
7676
fsid = self._proxy('GET', url, 'api/health/get_cluster_fsid', token=cluster_token)
7777

78+
managed_by_clusters_content = self._proxy('GET', url,
79+
'api/settings/MANAGED_BY_CLUSTERS',
80+
token=cluster_token)
81+
82+
managed_by_clusters_config = managed_by_clusters_content['value']
83+
84+
if managed_by_clusters_config is not None:
85+
managed_by_clusters_config.append({'url': hub_url, 'fsid': hub_fsid})
86+
87+
self._proxy('PUT', url, 'api/settings/MANAGED_BY_CLUSTERS',
88+
payload={'value': managed_by_clusters_config}, token=cluster_token,
89+
verify=ssl_verify, cert=ssl_certificate)
90+
7891
# add prometheus targets
7992
prometheus_url = self._proxy('GET', url, 'api/settings/PROMETHEUS_API_HOST',
8093
token=cluster_token)
94+
8195
_set_prometheus_targets(prometheus_url['value'])
8296

8397
self.set_multi_cluster_config(fsid, username, url, cluster_alias,
8498
cluster_token, prometheus_url['value'],
8599
ssl_verify, ssl_certificate)
86-
return
100+
return True
87101

88102
if token and cluster_fsid and prometheus_api_url:
89103
_set_prometheus_targets(prometheus_api_url)
90104
self.set_multi_cluster_config(cluster_fsid, username, url,
91105
cluster_alias, token, prometheus_api_url,
92106
ssl_verify, ssl_certificate)
107+
return True
108+
109+
def check_cluster_connection(self, url, payload, username, ssl_verify, ssl_certificate):
110+
try:
111+
content = self._proxy('POST', url, 'api/auth', payload=payload,
112+
verify=ssl_verify, cert=ssl_certificate)
113+
if 'token' not in content:
114+
raise DashboardException(msg=content['detail'], code='invalid_credentials',
115+
component='multi-cluster')
116+
117+
user_content = self._proxy('GET', url, f'api/user/{username}',
118+
token=content['token'])
119+
120+
if 'status' in user_content and user_content['status'] == '403 Forbidden':
121+
raise DashboardException(msg='User is not an administrator',
122+
code='invalid_permission', component='multi-cluster')
123+
if 'roles' in user_content and 'administrator' not in user_content['roles']:
124+
raise DashboardException(msg='User is not an administrator',
125+
code='invalid_permission', component='multi-cluster')
126+
127+
except Exception as e:
128+
if '[Errno 111] Connection refused' in str(e):
129+
raise DashboardException(msg='Connection refused',
130+
code='connection_refused', component='multi-cluster')
131+
raise DashboardException(msg=str(e), code='connection_failed',
132+
component='multi-cluster')
133+
134+
cluster_token = content['token']
135+
136+
managed_by_clusters_content = self._proxy('GET', url, 'api/settings/MANAGED_BY_CLUSTERS',
137+
token=cluster_token)
138+
139+
managed_by_clusters_config = managed_by_clusters_content['value']
140+
141+
if len(managed_by_clusters_config) > 1:
142+
raise DashboardException(msg='Cluster is already managed by another cluster',
143+
code='cluster_managed_by_another_cluster',
144+
component='multi-cluster')
145+
return cluster_token
93146

94147
def set_multi_cluster_config(self, fsid, username, url, cluster_alias, token,
95148
prometheus_url=None, ssl_verify=False, ssl_certificate=None):
@@ -144,7 +197,7 @@ def set_config(self, config: object):
144197

145198
@Endpoint('PUT')
146199
@UpdatePermission
147-
# pylint: disable=unused-variable
200+
# pylint: disable=W0613
148201
def reconnect_cluster(self, url: str, username=None, password=None, token=None,
149202
ssl_verify=False, ssl_certificate=None):
150203
multicluster_config = self.load_multi_cluster_config()
@@ -153,24 +206,18 @@ def reconnect_cluster(self, url: str, username=None, password=None, token=None,
153206
'username': username,
154207
'password': password
155208
}
156-
content = self._proxy('POST', url, 'api/auth', payload=payload,
157-
verify=ssl_verify, cert=ssl_certificate)
158-
if 'token' not in content:
159-
raise DashboardException(
160-
"Could not authenticate to remote cluster",
161-
http_status_code=400,
162-
component='dashboard')
163209

164-
token = content['token']
210+
cluster_token = self.check_cluster_connection(url, payload, username,
211+
ssl_verify, ssl_certificate)
165212

166-
if username and token:
213+
if username and cluster_token:
167214
if "config" in multicluster_config:
168215
for _, cluster_details in multicluster_config["config"].items():
169216
for cluster in cluster_details:
170217
if cluster["url"] == url and cluster["user"] == username:
171-
cluster['token'] = token
218+
cluster['token'] = cluster_token
172219
Settings.MULTICLUSTER_CONFIG = multicluster_config
173-
return Settings.MULTICLUSTER_CONFIG
220+
return True
174221

175222
@Endpoint('PUT')
176223
@UpdatePermission
@@ -189,10 +236,17 @@ def edit_cluster(self, url, cluster_alias, username):
189236
@DeletePermission
190237
def delete_cluster(self, cluster_name, cluster_user):
191238
multicluster_config = self.load_multi_cluster_config()
239+
try:
240+
hub_fsid = mgr.get('config')['fsid']
241+
except KeyError:
242+
hub_fsid = ''
192243
if "config" in multicluster_config:
193244
for key, value in list(multicluster_config['config'].items()):
194245
if value[0]['name'] == cluster_name and value[0]['user'] == cluster_user:
195-
246+
cluster_url = value[0]['url']
247+
cluster_token = value[0]['token']
248+
cluster_ssl_certificate = value[0]['ssl_certificate']
249+
cluster_ssl_verify = value[0]['ssl_verify']
196250
orch_backend = mgr.get_module_option_ex('orchestrator', 'orchestrator')
197251
try:
198252
if orch_backend == 'cephadm':
@@ -204,55 +258,25 @@ def delete_cluster(self, cluster_name, cluster_user):
204258
except KeyError:
205259
pass
206260

261+
managed_by_clusters_content = self._proxy('GET', cluster_url,
262+
'api/settings/MANAGED_BY_CLUSTERS',
263+
token=cluster_token)
264+
265+
managed_by_clusters_config = managed_by_clusters_content['value']
266+
for cluster in managed_by_clusters_config:
267+
if cluster['fsid'] == hub_fsid:
268+
managed_by_clusters_config.remove(cluster)
269+
270+
self._proxy('PUT', cluster_url, 'api/settings/MANAGED_BY_CLUSTERS',
271+
payload={'value': managed_by_clusters_config}, token=cluster_token,
272+
verify=cluster_ssl_verify, cert=cluster_ssl_certificate)
273+
207274
del multicluster_config['config'][key]
208275
break
209276

210277
Settings.MULTICLUSTER_CONFIG = multicluster_config
211278
return Settings.MULTICLUSTER_CONFIG
212279

213-
@Endpoint('POST')
214-
@CreatePermission
215-
# pylint: disable=R0911
216-
def verify_connection(self, url=None, username=None, password=None, token=None,
217-
ssl_verify=False, ssl_certificate=None):
218-
if token:
219-
try:
220-
payload = {
221-
'token': token
222-
}
223-
content = self._proxy('POST', url, 'api/auth/check', payload=payload,
224-
verify=ssl_verify, cert=ssl_certificate)
225-
if 'permissions' not in content:
226-
return content['detail']
227-
user_content = self._proxy('GET', url, f'api/user/{username}',
228-
token=content['token'])
229-
if 'status' in user_content and user_content['status'] == '403 Forbidden':
230-
return 'User is not an administrator'
231-
except Exception as e: # pylint: disable=broad-except
232-
if '[Errno 111] Connection refused' in str(e):
233-
return 'Connection refused'
234-
return 'Connection failed'
235-
236-
if username and password:
237-
try:
238-
payload = {
239-
'username': username,
240-
'password': password
241-
}
242-
content = self._proxy('POST', url, 'api/auth', payload=payload,
243-
verify=ssl_verify, cert=ssl_certificate)
244-
if 'token' not in content:
245-
return content['detail']
246-
user_content = self._proxy('GET', url, f'api/user/{username}',
247-
token=content['token'])
248-
if 'status' in user_content and user_content['status'] == '403 Forbidden':
249-
return 'User is not an administrator'
250-
except Exception as e: # pylint: disable=broad-except
251-
if '[Errno 111] Connection refused' in str(e):
252-
return 'Connection refused'
253-
return 'Connection failed'
254-
return 'Connection successful'
255-
256280
@Endpoint()
257281
@ReadPermission
258282
def get_config(self):

src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster-form/multi-cluster-form.component.html

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -220,40 +220,27 @@
220220
</div>
221221

222222
<!-- ssl_cert -->
223-
<div *ngIf="remoteClusterForm.controls.ssl.value"
224-
class="form-group row">
225-
<label class="cd-col-form-label"
226-
for="ssl_cert">
227-
<span i18n>Certificate</span>
228-
<cd-helper i18n>The SSL certificate in PEM format.</cd-helper>
229-
</label>
230-
<div class="cd-col-form-input">
231-
<textarea id="ssl_cert"
232-
class="form-control resize-vertical text-monospace text-pre"
233-
formControlName="ssl_cert"
234-
rows="5">
235-
</textarea>
236-
<input type="file"
237-
(change)="fileUpload($event.target.files, 'ssl_cert')">
238-
<span class="invalid-feedback"
239-
*ngIf="remoteClusterForm.showError('ssl_cert', frm, 'required')"
240-
i18n>This field is required.</span>
241-
<span class="invalid-feedback"
242-
*ngIf="remoteClusterForm.showError('ssl_cert', frm, 'pattern')"
243-
i18n>Invalid SSL certificate.</span>
244-
</div>
245-
</div>
246-
<div class="form-group row"
247-
*ngIf="!showCrossOriginError && action !== 'edit' && !remoteClusterForm.getValue('showToken') && !connectionVerified">
248-
<div class="cd-col-form-offset">
249-
<div class="custom-control">
250-
<button class="btn btn-primary"
251-
type="button"
252-
[disabled]="(remoteClusterForm.getValue('showToken') && remoteClusterForm.getValue('apiToken') === '') || (!remoteClusterForm.getValue('showToken') && (remoteClusterForm.getValue('username') === '' || remoteClusterForm.getValue('password') === ''))"
253-
(click)="verifyConnection()">
254-
Verify Connection
255-
</button>
256-
</div>
223+
<div *ngIf="remoteClusterForm.controls.ssl.value"
224+
class="form-group row">
225+
<label class="cd-col-form-label"
226+
for="ssl_cert">
227+
<span i18n>Certificate</span>
228+
<cd-helper i18n>The SSL certificate in PEM format.</cd-helper>
229+
</label>
230+
<div class="cd-col-form-input">
231+
<textarea id="ssl_cert"
232+
class="form-control resize-vertical text-monospace text-pre"
233+
formControlName="ssl_cert"
234+
rows="5">
235+
</textarea>
236+
<input type="file"
237+
(change)="fileUpload($event.target.files, 'ssl_cert')">
238+
<span class="invalid-feedback"
239+
*ngIf="remoteClusterForm.showError('ssl_cert', frm, 'required')"
240+
i18n>This field is required.</span>
241+
<span class="invalid-feedback"
242+
*ngIf="remoteClusterForm.showError('ssl_cert', frm, 'pattern')"
243+
i18n>Invalid SSL certificate.</span>
257244
</div>
258245
</div>
259246
</div>

0 commit comments

Comments
 (0)