Coverage for osm_nbi/engine.py: 26%

211 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-12 20:04 +0000

1# -*- coding: utf-8 -*- 

2 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 

12# implied. 

13# See the License for the specific language governing permissions and 

14# limitations under the License. 

15 

16import logging 

17 

18# import yaml 

19from osm_common import ( 

20 dbmongo, 

21 dbmemory, 

22 fslocal, 

23 fsmongo, 

24 msglocal, 

25 msgkafka, 

26 version as common_version, 

27) 

28from osm_common.dbbase import DbException 

29from osm_common.fsbase import FsException 

30from osm_common.msgbase import MsgException 

31from http import HTTPStatus 

32 

33from osm_nbi.authconn_keystone import AuthconnKeystone 

34from osm_nbi.authconn_internal import AuthconnInternal 

35from osm_nbi.authconn_tacacs import AuthconnTacacs 

36from osm_nbi.base_topic import EngineException, versiontuple 

37from osm_nbi.admin_topics import VimAccountTopic, WimAccountTopic, SdnTopic 

38from osm_nbi.admin_topics import K8sClusterTopic, K8sRepoTopic, OsmRepoTopic 

39from osm_nbi.admin_topics import VcaTopic 

40from osm_nbi.admin_topics import UserTopicAuth, ProjectTopicAuth, RoleTopicAuth 

41from osm_nbi.descriptor_topics import ( 

42 VnfdTopic, 

43 NsdTopic, 

44 PduTopic, 

45 NstTopic, 

46 VnfPkgOpTopic, 

47 NsConfigTemplateTopic, 

48) 

49from osm_nbi.instance_topics import ( 

50 NsrTopic, 

51 VnfrTopic, 

52 NsLcmOpTopic, 

53 NsiTopic, 

54 NsiLcmOpTopic, 

55) 

56from osm_nbi.k8s_topics import ( 

57 ClusterTopic, 

58 InfraContTopic, 

59 InfraConfTopic, 

60 AppTopic, 

61 ResourceTopic, 

62 ClusterOpsTopic, 

63 KsusTopic, 

64 OkaTopic, 

65) 

66from osm_nbi.vnf_instance_topics import VnfInstances, VnfLcmOpTopic 

67from osm_nbi.pmjobs_topics import PmJobsTopic 

68from osm_nbi.subscription_topics import NslcmSubscriptionsTopic 

69from osm_nbi.osm_vnfm.vnf_subscription import VnflcmSubscriptionsTopic 

70from base64 import b64encode 

71from os import urandom # , path 

72from threading import Lock 

73 

74__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>" 

75min_common_version = "0.1.16" 

76 

77 

78class Engine(object): 

79 map_from_topic_to_class = { 

80 "vnfds": VnfdTopic, 

81 "nsds": NsdTopic, 

82 "nsts": NstTopic, 

83 "pdus": PduTopic, 

84 "nsrs": NsrTopic, 

85 "vnfrs": VnfrTopic, 

86 "nslcmops": NsLcmOpTopic, 

87 "vim_accounts": VimAccountTopic, 

88 "wim_accounts": WimAccountTopic, 

89 "sdns": SdnTopic, 

90 "k8sclusters": K8sClusterTopic, 

91 "vca": VcaTopic, 

92 "k8srepos": K8sRepoTopic, 

93 "osmrepos": OsmRepoTopic, 

94 "users": UserTopicAuth, # Valid for both internal and keystone authentication backends 

95 "projects": ProjectTopicAuth, # Valid for both internal and keystone authentication backends 

96 "roles": RoleTopicAuth, # Valid for both internal and keystone authentication backends 

97 "nsis": NsiTopic, 

98 "nsilcmops": NsiLcmOpTopic, 

99 "vnfpkgops": VnfPkgOpTopic, 

100 "nslcm_subscriptions": NslcmSubscriptionsTopic, 

101 "vnf_instances": VnfInstances, 

102 "vnflcmops": VnfLcmOpTopic, 

103 "vnflcm_subscriptions": VnflcmSubscriptionsTopic, 

104 "nsconfigtemps": NsConfigTemplateTopic, 

105 "cluster": ClusterTopic, 

106 "infras_cont": InfraContTopic, 

107 "infras_conf": InfraConfTopic, 

108 "apps": AppTopic, 

109 "resources": ResourceTopic, 

110 "clusterops": ClusterOpsTopic, 

111 "ksus": KsusTopic, 

112 "oka_packages": OkaTopic, 

113 # [NEW_TOPIC]: add an entry here 

114 # "pm_jobs": PmJobsTopic will be added manually because it needs other parameters 

115 } 

116 

117 map_target_version_to_int = { 

118 "1.0": 1000, 

119 "1.1": 1001, 

120 "1.2": 1002, 

121 # Add new versions here 

122 } 

123 

124 def __init__(self, authenticator): 

125 self.db = None 

126 self.fs = None 

127 self.msg = None 

128 self.authconn = None 

129 self.config = None 

130 # self.operations = None 

131 self.logger = logging.getLogger("nbi.engine") 

132 self.map_topic = {} 

133 self.write_lock = None 

134 # self.token_cache = token_cache 

135 self.authenticator = authenticator 

136 

137 def start(self, config): 

138 """ 

139 Connect to database, filesystem storage, and messaging 

140 :param config: two level dictionary with configuration. Top level should contain 'database', 'storage', 

141 :return: None 

142 """ 

143 self.config = config 

144 # check right version of common 

145 if versiontuple(common_version) < versiontuple(min_common_version): 

146 raise EngineException( 

147 "Not compatible osm/common version '{}'. Needed '{}' or higher".format( 

148 common_version, min_common_version 

149 ) 

150 ) 

151 

152 try: 

153 if not self.db: 

154 if config["database"]["driver"] == "mongo": 

155 self.db = dbmongo.DbMongo() 

156 self.db.db_connect(config["database"]) 

157 elif config["database"]["driver"] == "memory": 

158 self.db = dbmemory.DbMemory() 

159 self.db.db_connect(config["database"]) 

160 else: 

161 raise EngineException( 

162 "Invalid configuration param '{}' at '[database]':'driver'".format( 

163 config["database"]["driver"] 

164 ) 

165 ) 

166 if not self.fs: 

167 if config["storage"]["driver"] == "local": 

168 self.fs = fslocal.FsLocal() 

169 self.fs.fs_connect(config["storage"]) 

170 elif config["storage"]["driver"] == "mongo": 

171 self.fs = fsmongo.FsMongo() 

172 self.fs.fs_connect(config["storage"]) 

173 else: 

174 raise EngineException( 

175 "Invalid configuration param '{}' at '[storage]':'driver'".format( 

176 config["storage"]["driver"] 

177 ) 

178 ) 

179 if not self.msg: 

180 if config["message"]["driver"] == "local": 

181 self.msg = msglocal.MsgLocal() 

182 self.msg.connect(config["message"]) 

183 elif config["message"]["driver"] == "kafka": 

184 self.msg = msgkafka.MsgKafka() 

185 self.msg.connect(config["message"]) 

186 else: 

187 raise EngineException( 

188 "Invalid configuration param '{}' at '[message]':'driver'".format( 

189 config["message"]["driver"] 

190 ) 

191 ) 

192 if not self.authconn: 

193 if config["authentication"]["backend"] == "keystone": 

194 self.authconn = AuthconnKeystone( 

195 config["authentication"], 

196 self.db, 

197 self.authenticator.role_permissions, 

198 ) 

199 elif config["authentication"]["backend"] == "tacacs": 

200 self.authconn = AuthconnTacacs( 

201 config["authentication"], 

202 self.db, 

203 self.authenticator.role_permissions, 

204 ) 

205 else: 

206 self.authconn = AuthconnInternal( 

207 config["authentication"], 

208 self.db, 

209 self.authenticator.role_permissions, 

210 ) 

211 # if not self.operations: 

212 # if "resources_to_operations" in config["rbac"]: 

213 # resources_to_operations_file = config["rbac"]["resources_to_operations"] 

214 # else: 

215 # possible_paths = ( 

216 # __file__[:__file__.rfind("engine.py")] + "resources_to_operations.yml", 

217 # "./resources_to_operations.yml" 

218 # ) 

219 # for config_file in possible_paths: 

220 # if path.isfile(config_file): 

221 # resources_to_operations_file = config_file 

222 # break 

223 # if not resources_to_operations_file: 

224 # raise EngineException("Invalid permission configuration:" 

225 # "resources_to_operations file missing") 

226 # 

227 # with open(resources_to_operations_file, 'r') as f: 

228 # resources_to_operations = yaml.safeload(f) 

229 # 

230 # self.operations = [] 

231 # 

232 # for _, value in resources_to_operations["resources_to_operations"].items(): 

233 # if value not in self.operations: 

234 # self.operations += [value] 

235 

236 self.write_lock = Lock() 

237 # create one class per topic 

238 for topic, topic_class in self.map_from_topic_to_class.items(): 

239 # if self.auth and topic_class in (UserTopicAuth, ProjectTopicAuth): 

240 # self.map_topic[topic] = topic_class(self.db, self.fs, self.msg, self.auth) 

241 self.map_topic[topic] = topic_class( 

242 self.db, self.fs, self.msg, self.authconn 

243 ) 

244 

245 self.map_topic["pm_jobs"] = PmJobsTopic( 

246 self.db, 

247 config["prometheus"].get("host"), 

248 config["prometheus"].get("port"), 

249 ) 

250 except (DbException, FsException, MsgException) as e: 

251 raise EngineException(str(e), http_code=e.http_code) 

252 

253 def stop(self): 

254 try: 

255 if self.db: 

256 self.db.db_disconnect() 

257 if self.fs: 

258 self.fs.fs_disconnect() 

259 if self.msg: 

260 self.msg.disconnect() 

261 self.write_lock = None 

262 except (DbException, FsException, MsgException) as e: 

263 raise EngineException(str(e), http_code=e.http_code) 

264 

265 def new_item( 

266 self, rollback, session, topic, indata=None, kwargs=None, headers=None 

267 ): 

268 """ 

269 Creates a new entry into database. For nsds and vnfds it creates an almost empty DISABLED entry, 

270 that must be completed with a call to method upload_content 

271 :param rollback: list to append created items at database in case a rollback must to be done 

272 :param session: contains the used login username and working project, force to avoid checkins, public 

273 :param topic: it can be: users, projects, vim_accounts, sdns, nsrs, nsds, vnfds 

274 :param indata: data to be inserted 

275 :param kwargs: used to override the indata descriptor 

276 :param headers: http request headers 

277 :return: _id: identity of the inserted data. 

278 """ 

279 if topic not in self.map_topic: 

280 raise EngineException( 

281 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

282 ) 

283 with self.write_lock: 

284 return self.map_topic[topic].new(rollback, session, indata, kwargs, headers) 

285 

286 def add_item( 

287 self, rollback, session, topic, indata=None, kwargs=None, headers=None 

288 ): 

289 """ 

290 register a cluster in the database. 

291 :param rollback: list to append created items at database in case a rollback must to be done 

292 :param session: contains the used login username and working project, force to avoid checkins, public 

293 :param topic: it can be: cluster for adding cluster into database 

294 :param indata: data to be inserted 

295 :param kwargs: used to override the indata descriptor 

296 :param headers: http request headers 

297 :return: _id: identity of the inserted data. 

298 """ 

299 if topic not in self.map_topic: 

300 raise EngineException( 

301 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

302 ) 

303 with self.write_lock: 

304 return self.map_topic[topic].add(rollback, session, indata, kwargs, headers) 

305 

306 def upload_content(self, session, topic, _id, indata, kwargs, headers): 

307 """ 

308 Upload content for an already created entry (_id) 

309 :param session: contains the used login username and working project 

310 :param topic: it can be: users, projects, vnfds, nsds, 

311 :param _id: server id of the item 

312 :param indata: data to be inserted 

313 :param kwargs: used to override the indata descriptor 

314 :param headers: http request headers 

315 :return: _id: identity of the inserted data. 

316 """ 

317 if topic not in self.map_topic: 

318 raise EngineException( 

319 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

320 ) 

321 with self.write_lock: 

322 return self.map_topic[topic].upload_content( 

323 session, _id, indata, kwargs, headers 

324 ) 

325 

326 def clone( 

327 self, rollback, session, topic, _id, indata=None, kwargs=None, headers=None 

328 ): 

329 if topic not in self.map_topic: 

330 raise EngineException( 

331 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

332 ) 

333 with self.write_lock: 

334 return self.map_topic[topic].clone( 

335 rollback, session, _id, indata, kwargs, headers 

336 ) 

337 

338 def move_ksu(self, session, topic, _id, indata=None, kwargs=None): 

339 if topic not in self.map_topic: 

340 raise EngineException( 

341 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

342 ) 

343 

344 with self.write_lock: 

345 return self.map_topic[topic].move_ksu(session, _id, indata, kwargs) 

346 

347 def get_cluster_creds_file(self, session, topic, _id, item, op_id): 

348 if topic not in self.map_topic: 

349 raise EngineException( 

350 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

351 ) 

352 return self.map_topic[topic].get_cluster_creds_file(session, _id, item, op_id) 

353 

354 def get_cluster_creds(self, session, topic, _id, item): 

355 if topic not in self.map_topic: 

356 raise EngineException( 

357 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

358 ) 

359 return self.map_topic[topic].get_cluster_creds(session, _id, item) 

360 

361 def update_cluster(self, session, topic, _id, item, indata): 

362 if topic not in self.map_topic: 

363 raise EngineException( 

364 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

365 ) 

366 return self.map_topic[topic].update_cluster(session, _id, item, indata) 

367 

368 def delete_ksu(self, session, topic, _id, indata): 

369 if topic not in self.map_topic: 

370 raise EngineException( 

371 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

372 ) 

373 with self.write_lock: 

374 return self.map_topic[topic].delete_ksu( 

375 session, _id, indata, not_send_msg=None 

376 ) 

377 

378 def get_item_list(self, session, topic, filter_q=None, api_req=False): 

379 """ 

380 Get a list of items 

381 :param session: contains the used login username and working project 

382 :param topic: it can be: users, projects, vnfds, nsds, ... 

383 :param filter_q: filter of data to be applied 

384 :param api_req: True if this call is serving an external API request. False if serving internal request. 

385 :return: The list, it can be empty if no one match the filter_q. 

386 """ 

387 self.logger.info("it is getting into item list") 

388 if topic not in self.map_topic: 

389 raise EngineException( 

390 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

391 ) 

392 return self.map_topic[topic].list(session, filter_q, api_req) 

393 

394 def get_item_list_cluster(self, session, topic, filter_q=None, api_req=False): 

395 """ 

396 Get a list of items 

397 :param session: contains the used login username and working project 

398 :param topic: it can be: users, projects, vnfds, nsds, ... 

399 :param filter_q: filter of data to be applied 

400 :param api_req: True if this call is serving an external API request. False if serving internal request. 

401 :return: The list, it can be empty if no one match the filter_q. 

402 """ 

403 self.logger.info("it is getting into item list cluster") 

404 if topic not in self.map_topic: 

405 raise EngineException( 

406 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

407 ) 

408 return self.map_topic[topic].list_both(session, filter_q, api_req) 

409 

410 def get_item(self, session, topic, _id, filter_q=None, api_req=False): 

411 """ 

412 Get complete information on an item 

413 :param session: contains the used login username and working project 

414 :param topic: it can be: users, projects, vnfds, nsds, clusters, 

415 :param _id: server id of the item 

416 :param filter_q: other arguments 

417 :param api_req: True if this call is serving an external API request. False if serving internal request. 

418 :return: dictionary, raise exception if not found. 

419 """ 

420 if topic not in self.map_topic: 

421 raise EngineException( 

422 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

423 ) 

424 return self.map_topic[topic].show(session, _id, filter_q, api_req) 

425 

426 def get_one_item(self, session, topic, _id, profile, filter_q=None, api_req=False): 

427 """ 

428 Get complete information on an item 

429 :param session: contains the used login username and working project 

430 :param topic: it can be: users, projects, vnfds, nsds, clusters profile, 

431 :param _id: server id of the item 

432 :param profile: contains the profile type 

433 :param filter_q: other arguments 

434 :param api_req: True if this call is serving an external API request. False if serving internal request. 

435 :return: dictionary, raise exception if not found. 

436 """ 

437 if topic not in self.map_topic: 

438 raise EngineException( 

439 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

440 ) 

441 return self.map_topic[topic].show_one(session, _id, profile, filter_q, api_req) 

442 

443 def get_file(self, session, topic, _id, path=None, accept_header=None): 

444 """ 

445 Get descriptor package or artifact file content 

446 :param session: contains the used login username and working project 

447 :param topic: it can be: users, projects, vnfds, nsds, 

448 :param _id: server id of the item 

449 :param path: artifact path or "$DESCRIPTOR" or None 

450 :param accept_header: Content of Accept header. Must contain applition/zip or/and text/plain 

451 :return: opened file plus Accept format or raises an exception 

452 """ 

453 if topic not in self.map_topic: 

454 raise EngineException( 

455 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

456 ) 

457 return self.map_topic[topic].get_file(session, _id, path, accept_header) 

458 

459 def del_item_list(self, session, topic, _filter=None): 

460 """ 

461 Delete a list of items 

462 :param session: contains the used login username and working project 

463 :param topic: it can be: users, projects, vnfds, nsds, ... 

464 :param _filter: filter of data to be applied 

465 :return: The deleted list, it can be empty if no one match the _filter. 

466 """ 

467 if topic not in self.map_topic: 

468 raise EngineException( 

469 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

470 ) 

471 with self.write_lock: 

472 return self.map_topic[topic].delete_list(session, _filter) 

473 

474 def del_item(self, session, topic, _id, not_send_msg=None): 

475 """ 

476 Delete item by its internal id 

477 :param session: contains the used login username and working project 

478 :param topic: it can be: users, projects, vnfds, nsds, ... 

479 :param _id: server id of the item 

480 :param not_send_msg: If False, message will not be sent to kafka. 

481 If a list, message is not sent, but content is stored in this variable so that the caller can send this 

482 message using its own loop. If None, message is sent 

483 :return: dictionary with deleted item _id. It raises exception if not found. 

484 """ 

485 if topic not in self.map_topic: 

486 raise EngineException( 

487 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

488 ) 

489 with self.write_lock: 

490 return self.map_topic[topic].delete(session, _id, not_send_msg=not_send_msg) 

491 

492 def remove(self, session, topic, _id, not_send_msg=None): 

493 """ 

494 Delete item by its internal id 

495 :param session: contains the used login username and working project 

496 :param topic: it can be: users, projects, vnfds, nsds, clusters, 

497 :param _id: server id of the item 

498 :param not_send_msg: If False, message will not be sent to kafka. 

499 If a list, message is not sent, but content is stored in this variable so that the caller can send this 

500 message using its own loop. If None, message is sent 

501 :return: dictionary with deleted item _id. It raises exception if not found. 

502 """ 

503 if topic not in self.map_topic: 

504 raise EngineException( 

505 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

506 ) 

507 with self.write_lock: 

508 return self.map_topic[topic].remove(session, _id, not_send_msg=not_send_msg) 

509 

510 def edit_item(self, session, topic, _id, indata=None, kwargs=None): 

511 """ 

512 Update an existing entry at database 

513 :param session: contains the used login username and working project 

514 :param topic: it can be: users, projects, vnfds, nsds, ... 

515 :param _id: identifier to be updated 

516 :param indata: data to be inserted 

517 :param kwargs: used to override the indata descriptor 

518 :return: dictionary with edited item _id, raise exception if not found. 

519 """ 

520 if topic not in self.map_topic: 

521 raise EngineException( 

522 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

523 ) 

524 with self.write_lock: 

525 return self.map_topic[topic].edit(session, _id, indata, kwargs) 

526 

527 def edit(self, session, topic, _id, item, indata=None, kwargs=None): 

528 """ 

529 Update an existing entry at database 

530 :param session: contains the used login username and working project 

531 :param topic: it can be: users, projects, vnfds, nsds, ... 

532 :param _id: identifier to be updated 

533 :param item: it shows the type of profiles 

534 :param indata: data to be inserted 

535 :param kwargs: used to override the indata descriptor 

536 :return: dictionary with edited item _id, raise exception if not found. 

537 """ 

538 if topic not in self.map_topic: 

539 raise EngineException( 

540 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

541 ) 

542 with self.write_lock: 

543 return self.map_topic[topic].edit(session, _id, item, indata, kwargs) 

544 

545 def cancel_item( 

546 self, rollback, session, topic, indata=None, kwargs=None, headers=None 

547 ): 

548 """ 

549 Cancels an item 

550 :param rollback: list to append created items at database in case a rollback must to be done 

551 :param session: contains the used login username and working project, force to avoid checkins, public 

552 :param topic: it can be: users, projects, vim_accounts, sdns, nsrs, nsds, vnfds 

553 :param indata: data to be inserted 

554 :param kwargs: used to override the indata descriptor 

555 :param headers: http request headers 

556 :return: _id: identity of the inserted data. 

557 """ 

558 if topic not in self.map_topic: 

559 raise EngineException( 

560 "Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR 

561 ) 

562 with self.write_lock: 

563 self.map_topic[topic].cancel(rollback, session, indata, kwargs, headers) 

564 

565 def upgrade_db(self, current_version, target_version): 

566 if target_version not in self.map_target_version_to_int.keys(): 

567 raise EngineException( 

568 "Cannot upgrade to version '{}' with this version of code".format( 

569 target_version 

570 ), 

571 http_code=HTTPStatus.INTERNAL_SERVER_ERROR, 

572 ) 

573 

574 if current_version == target_version: 

575 return 

576 

577 target_version_int = self.map_target_version_to_int[target_version] 

578 

579 if not current_version: 

580 # create database version 

581 serial = urandom(32) 

582 version_data = { 

583 "_id": "version", # Always "version" 

584 "version_int": 1000, # version number 

585 "version": "1.0", # version text 

586 "date": "2018-10-25", # version date 

587 "description": "added serial", # changes in this version 

588 "status": "ENABLED", # ENABLED, DISABLED (migration in process), ERROR, 

589 "serial": b64encode(serial), 

590 } 

591 self.db.create("admin", version_data) 

592 self.db.set_secret_key(serial) 

593 current_version = "1.0" 

594 

595 if ( 

596 current_version in ("1.0", "1.1") 

597 and target_version_int >= self.map_target_version_to_int["1.2"] 

598 ): 

599 if self.config["authentication"]["backend"] == "internal": 

600 self.db.del_list("roles") 

601 

602 version_data = { 

603 "_id": "version", 

604 "version_int": 1002, 

605 "version": "1.2", 

606 "date": "2019-06-11", 

607 "description": "set new format for roles_operations", 

608 } 

609 

610 self.db.set_one("admin", {"_id": "version"}, version_data) 

611 current_version = "1.2" 

612 # TODO add future migrations here 

613 

614 def init_db(self, target_version="1.0"): 

615 """ 

616 Init database if empty. If not empty it checks that database version and migrates if needed 

617 If empty, it creates a new user admin/admin at 'users' and a new entry at 'version' 

618 :param target_version: check desired database version. Migrate to it if possible or raises exception 

619 :return: None if ok, exception if error or if the version is different. 

620 """ 

621 

622 version_data = self.db.get_one( 

623 "admin", {"_id": "version"}, fail_on_empty=False, fail_on_more=True 

624 ) 

625 # check database status is ok 

626 if version_data and version_data.get("status") != "ENABLED": 

627 raise EngineException( 

628 "Wrong database status '{}'".format(version_data["status"]), 

629 HTTPStatus.INTERNAL_SERVER_ERROR, 

630 ) 

631 

632 # check version 

633 db_version = None if not version_data else version_data.get("version") 

634 if db_version != target_version: 

635 self.upgrade_db(db_version, target_version) 

636 

637 return