Coverage for osm_nbi/nbi.py: 0%

949 statements  

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

1#!/usr/bin/python3 

2# -*- coding: utf-8 -*- 

3 

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

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

6# You may obtain a copy of the License at 

7# 

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

9# 

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

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

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

13# implied. 

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

15# limitations under the License. 

16 

17import cherrypy 

18import time 

19import json 

20import yaml 

21import osm_nbi.html_out as html 

22import logging 

23import logging.handlers 

24import getopt 

25import sys 

26 

27from osm_nbi.authconn import AuthException, AuthconnException 

28from osm_nbi.auth import Authenticator 

29from osm_nbi.engine import Engine, EngineException 

30from osm_nbi.subscriptions import SubscriptionThread 

31from osm_nbi.utils import cef_event, cef_event_builder 

32from osm_nbi.validation import ValidationError 

33from osm_common.dbbase import DbException 

34from osm_common.fsbase import FsException 

35from osm_common.msgbase import MsgException 

36from http import HTTPStatus 

37from codecs import getreader 

38from os import environ, path 

39from osm_nbi import version as nbi_version, version_date as nbi_version_date 

40 

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

42 

43__version__ = "0.1.3" # file version, not NBI version 

44version_date = "Aug 2019" 

45 

46database_version = "1.2" 

47auth_database_version = "1.0" 

48nbi_server = None # instance of Server class 

49subscription_thread = None # instance of SubscriptionThread class 

50cef_logger = None 

51logger = logging.getLogger("nbi.nbi") 

52 

53""" 

54North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented) 

55URL: /osm GET POST PUT DELETE PATCH 

56 /nsd/v1 

57 /ns_descriptors_content O O 

58 /<nsdInfoId> O O O O 

59 /ns_descriptors O5 O5 

60 /<nsdInfoId> O5 O5 5 

61 /nsd_content O5 O5 

62 /nsd O 

63 /artifacts[/<artifactPath>] O 

64 /ns_config_template O O 

65 /<nsConfigTemplateId> O O 

66 /template_content O O 

67 /pnf_descriptors 5 5 

68 /<pnfdInfoId> 5 5 5 

69 /pnfd_content 5 5 

70 /subscriptions 5 5 

71 /<subscriptionId> 5 X 

72 

73 /vnfpkgm/v1 

74 /vnf_packages_content O O 

75 /<vnfPkgId> O O 

76 /vnf_packages O5 O5 

77 /<vnfPkgId> O5 O5 5 

78 /package_content O5 O5 

79 /upload_from_uri X 

80 /vnfd O5 

81 /artifacts[/<artifactPath>] O5 

82 /subscriptions X X 

83 /<subscriptionId> X X 

84 

85 /nslcm/v1 

86 /ns_instances_content O O 

87 /<nsInstanceId> O O 

88 /ns_instances 5 5 

89 /<nsInstanceId> O5 O5 

90 instantiate O5 

91 terminate O5 

92 action O 

93 scale O5 

94 migrate O 

95 update 05 

96 heal O5 

97 /ns_lcm_op_occs 5 5 

98 /<nsLcmOpOccId> 5 5 5 

99 cancel 05 

100 /vnf_instances (also vnfrs for compatibility) O 

101 /<vnfInstanceId> O 

102 /subscriptions 5 5 

103 /<subscriptionId> 5 X 

104 

105 /pdu/v1 

106 /pdu_descriptors O O 

107 /<id> O O O O 

108 

109 /admin/v1 

110 /tokens O O 

111 /<id> O O 

112 /users O O 

113 /<id> O O O O 

114 /projects O O 

115 /<id> O O 

116 /vim_accounts (also vims for compatibility) O O 

117 /<id> O O O 

118 /wim_accounts O O 

119 /<id> O O O 

120 /sdns O O 

121 /<id> O O O 

122 /k8sclusters O O 

123 /<id> O O O 

124 /k8srepos O O 

125 /<id> O O 

126 /osmrepos O O 

127 /<id> O O 

128 

129 /nst/v1 O O 

130 /netslice_templates_content O O 

131 /<nstInfoId> O O O O 

132 /netslice_templates O O 

133 /<nstInfoId> O O O 

134 /nst_content O O 

135 /nst O 

136 /artifacts[/<artifactPath>] O 

137 /subscriptions X X 

138 /<subscriptionId> X X 

139 

140 /nsilcm/v1 

141 /netslice_instances_content O O 

142 /<SliceInstanceId> O O 

143 /netslice_instances O O 

144 /<SliceInstanceId> O O 

145 instantiate O 

146 terminate O 

147 action O 

148 /nsi_lcm_op_occs O O 

149 /<nsiLcmOpOccId> O O O 

150 /subscriptions X X 

151 /<subscriptionId> X X 

152 

153 /k8scluster/v1 

154 /clusters O O 

155 /<clustersId> O O 

156 app_profiles O O 

157 infra_controller_profiles O O 

158 infra_config_profiles O O 

159 resource_profiles O O 

160 deregister O 

161 /register O 

162 /app_profiles O O 

163 /<app_profilesId> O O O 

164 /infra_controller_profiles O O 

165 /<infra_controller_profilesId> O O O 

166 /infra_config_profiles O O 

167 /<infra_config_profilesId> O O O 

168 /resource_profiles O O 

169 /<resource_profilesID> O O O 

170 

171query string: 

172 Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force. 

173 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]* 

174 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]* 

175 op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont" 

176 attrName := string 

177 For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any 

178 item of the array, that is, pass if any item of the array pass the filter. 

179 It allows both ne and neq for not equal 

180 TODO: 4.3.3 Attribute selectors 

181 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,... 

182 (none) … same as “exclude_default” 

183 all_fields … all attributes. 

184 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not 

185 conditionally mandatory, and that are not provided in <list>. 

186 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that 

187 are not conditionally mandatory, and that are provided in <list>. 

188 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not 

189 conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for 

190 the particular resource 

191 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality 

192 of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the 

193 present specification for the particular resource, but that are not part of <list> 

194 Additionally it admits some administrator values: 

195 FORCE: To force operations skipping dependency checkings 

196 ADMIN: To act as an administrator or a different project 

197 PUBLIC: To get public descriptors or set a descriptor as public 

198 SET_PROJECT: To make a descriptor available for other project 

199 

200Header field name Reference Example Descriptions 

201 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response. 

202 This header field shall be present if the response is expected to have a non-empty message body. 

203 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request. 

204 This header field shall be present if the request has a non-empty message body. 

205 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request. 

206 Details are specified in clause 4.5.3. 

207 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file 

208Header field name Reference Example Descriptions 

209 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response. 

210 This header field shall be present if the response has a non-empty message body. 

211 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a 

212 new resource has been created. 

213 This header field shall be present if the response status code is 201 or 3xx. 

214 In the present document this header field is also used if the response status code is 202 and a new resource was 

215 created. 

216 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not 

217 provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization 

218 token. 

219 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for 

220 certain resources. 

221 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the 

222 response, and the total length of the file. 

223 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT 

224""" 

225 

226valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC") 

227# ^ Contains possible administrative query string words: 

228# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project 

229# (not owned by my session project). 

230# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public 

231# FORCE=True(by default)|False: Force edition/deletion operations 

232# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio 

233 

234valid_url_methods = { 

235 # contains allowed URL and methods, and the role_permission name 

236 "admin": { 

237 "v1": { 

238 "tokens": { 

239 "METHODS": ("GET", "POST", "DELETE"), 

240 "ROLE_PERMISSION": "tokens:", 

241 "<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"}, 

242 }, 

243 "users": { 

244 "METHODS": ("GET", "POST"), 

245 "ROLE_PERMISSION": "users:", 

246 "<ID>": { 

247 "METHODS": ("GET", "DELETE", "PATCH"), 

248 "ROLE_PERMISSION": "users:id:", 

249 }, 

250 }, 

251 "projects": { 

252 "METHODS": ("GET", "POST"), 

253 "ROLE_PERMISSION": "projects:", 

254 "<ID>": { 

255 "METHODS": ("GET", "DELETE", "PATCH"), 

256 "ROLE_PERMISSION": "projects:id:", 

257 }, 

258 }, 

259 "roles": { 

260 "METHODS": ("GET", "POST"), 

261 "ROLE_PERMISSION": "roles:", 

262 "<ID>": { 

263 "METHODS": ("GET", "DELETE", "PATCH"), 

264 "ROLE_PERMISSION": "roles:id:", 

265 }, 

266 }, 

267 "vims": { 

268 "METHODS": ("GET", "POST"), 

269 "ROLE_PERMISSION": "vims:", 

270 "<ID>": { 

271 "METHODS": ("GET", "DELETE", "PATCH"), 

272 "ROLE_PERMISSION": "vims:id:", 

273 }, 

274 }, 

275 "vim_accounts": { 

276 "METHODS": ("GET", "POST"), 

277 "ROLE_PERMISSION": "vim_accounts:", 

278 "<ID>": { 

279 "METHODS": ("GET", "DELETE", "PATCH"), 

280 "ROLE_PERMISSION": "vim_accounts:id:", 

281 }, 

282 }, 

283 "wim_accounts": { 

284 "METHODS": ("GET", "POST"), 

285 "ROLE_PERMISSION": "wim_accounts:", 

286 "<ID>": { 

287 "METHODS": ("GET", "DELETE", "PATCH"), 

288 "ROLE_PERMISSION": "wim_accounts:id:", 

289 }, 

290 }, 

291 "sdns": { 

292 "METHODS": ("GET", "POST"), 

293 "ROLE_PERMISSION": "sdn_controllers:", 

294 "<ID>": { 

295 "METHODS": ("GET", "DELETE", "PATCH"), 

296 "ROLE_PERMISSION": "sdn_controllers:id:", 

297 }, 

298 }, 

299 "k8sclusters": { 

300 "METHODS": ("GET", "POST"), 

301 "ROLE_PERMISSION": "k8sclusters:", 

302 "<ID>": { 

303 "METHODS": ("GET", "DELETE", "PATCH"), 

304 "ROLE_PERMISSION": "k8sclusters:id:", 

305 }, 

306 }, 

307 "vca": { 

308 "METHODS": ("GET", "POST"), 

309 "ROLE_PERMISSION": "vca:", 

310 "<ID>": { 

311 "METHODS": ("GET", "DELETE", "PATCH"), 

312 "ROLE_PERMISSION": "vca:id:", 

313 }, 

314 }, 

315 "k8srepos": { 

316 "METHODS": ("GET", "POST"), 

317 "ROLE_PERMISSION": "k8srepos:", 

318 "<ID>": { 

319 "METHODS": ("GET", "DELETE"), 

320 "ROLE_PERMISSION": "k8srepos:id:", 

321 }, 

322 }, 

323 "osmrepos": { 

324 "METHODS": ("GET", "POST"), 

325 "ROLE_PERMISSION": "osmrepos:", 

326 "<ID>": { 

327 "METHODS": ("GET", "DELETE", "PATCH"), 

328 "ROLE_PERMISSION": "osmrepos:id:", 

329 }, 

330 }, 

331 "domains": { 

332 "METHODS": ("GET",), 

333 "ROLE_PERMISSION": "domains:", 

334 }, 

335 } 

336 }, 

337 "pdu": { 

338 "v1": { 

339 "pdu_descriptors": { 

340 "METHODS": ("GET", "POST"), 

341 "ROLE_PERMISSION": "pduds:", 

342 "<ID>": { 

343 "METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"), 

344 "ROLE_PERMISSION": "pduds:id:", 

345 }, 

346 }, 

347 } 

348 }, 

349 "nsd": { 

350 "v1": { 

351 "ns_descriptors_content": { 

352 "METHODS": ("GET", "POST"), 

353 "ROLE_PERMISSION": "nsds:content:", 

354 "<ID>": { 

355 "METHODS": ("GET", "PUT", "DELETE"), 

356 "ROLE_PERMISSION": "nsds:id:", 

357 }, 

358 }, 

359 "ns_descriptors": { 

360 "METHODS": ("GET", "POST"), 

361 "ROLE_PERMISSION": "nsds:", 

362 "<ID>": { 

363 "METHODS": ("GET", "DELETE", "PATCH"), 

364 "ROLE_PERMISSION": "nsds:id:", 

365 "nsd_content": { 

366 "METHODS": ("GET", "PUT"), 

367 "ROLE_PERMISSION": "nsds:id:content:", 

368 }, 

369 "nsd": { 

370 "METHODS": ("GET",), # descriptor inside package 

371 "ROLE_PERMISSION": "nsds:id:nsd:", 

372 }, 

373 "artifacts": { 

374 "METHODS": ("GET",), 

375 "ROLE_PERMISSION": "nsds:id:nsd_artifact:", 

376 "*": None, 

377 }, 

378 }, 

379 }, 

380 "ns_config_template": { 

381 "METHODS": ("GET", "POST"), 

382 "ROLE_PERMISSION": "ns_config_template:content:", 

383 "<ID>": { 

384 "METHODS": ("GET", "DELETE"), 

385 "ROLE_PERMISSION": "ns_config_template:id:", 

386 "template_content": { 

387 "METHODS": ("GET", "PUT"), 

388 "ROLE_PERMISSION": "ns_config_template:id:content:", 

389 }, 

390 }, 

391 }, 

392 "pnf_descriptors": { 

393 "TODO": ("GET", "POST"), 

394 "<ID>": { 

395 "TODO": ("GET", "DELETE", "PATCH"), 

396 "pnfd_content": {"TODO": ("GET", "PUT")}, 

397 }, 

398 }, 

399 "subscriptions": { 

400 "TODO": ("GET", "POST"), 

401 "<ID>": {"TODO": ("GET", "DELETE")}, 

402 }, 

403 } 

404 }, 

405 "vnfpkgm": { 

406 "v1": { 

407 "vnf_packages_content": { 

408 "METHODS": ("GET", "POST"), 

409 "ROLE_PERMISSION": "vnfds:content:", 

410 "<ID>": { 

411 "METHODS": ("GET", "PUT", "DELETE"), 

412 "ROLE_PERMISSION": "vnfds:id:", 

413 }, 

414 }, 

415 "vnf_packages": { 

416 "METHODS": ("GET", "POST"), 

417 "ROLE_PERMISSION": "vnfds:", 

418 "<ID>": { 

419 "METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo 

420 "ROLE_PERMISSION": "vnfds:id:", 

421 "package_content": { 

422 "METHODS": ("GET", "PUT"), # package 

423 "ROLE_PERMISSION": "vnfds:id:content:", 

424 "upload_from_uri": { 

425 "METHODS": (), 

426 "TODO": ("POST",), 

427 "ROLE_PERMISSION": "vnfds:id:upload:", 

428 }, 

429 }, 

430 "vnfd": { 

431 "METHODS": ("GET",), # descriptor inside package 

432 "ROLE_PERMISSION": "vnfds:id:vnfd:", 

433 }, 

434 "artifacts": { 

435 "METHODS": ("GET",), 

436 "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:", 

437 "*": None, 

438 }, 

439 "action": { 

440 "METHODS": ("POST",), 

441 "ROLE_PERMISSION": "vnfds:id:action:", 

442 }, 

443 }, 

444 }, 

445 "subscriptions": { 

446 "TODO": ("GET", "POST"), 

447 "<ID>": {"TODO": ("GET", "DELETE")}, 

448 }, 

449 "vnfpkg_op_occs": { 

450 "METHODS": ("GET",), 

451 "ROLE_PERMISSION": "vnfds:vnfpkgops:", 

452 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"}, 

453 }, 

454 } 

455 }, 

456 "nslcm": { 

457 "v1": { 

458 "ns_instances_terminate": { 

459 "METHODS": ("POST"), 

460 "ROLE_PERMISSION": "ns_instances:", 

461 }, 

462 "ns_instances_content": { 

463 "METHODS": ("GET", "POST"), 

464 "ROLE_PERMISSION": "ns_instances:content:", 

465 "<ID>": { 

466 "METHODS": ("GET", "DELETE"), 

467 "ROLE_PERMISSION": "ns_instances:id:", 

468 }, 

469 }, 

470 "ns_instances": { 

471 "METHODS": ("GET", "POST"), 

472 "ROLE_PERMISSION": "ns_instances:", 

473 "<ID>": { 

474 "METHODS": ("GET", "DELETE"), 

475 "ROLE_PERMISSION": "ns_instances:id:", 

476 "heal": { 

477 "METHODS": ("POST",), 

478 "ROLE_PERMISSION": "ns_instances:id:heal:", 

479 }, 

480 "scale": { 

481 "METHODS": ("POST",), 

482 "ROLE_PERMISSION": "ns_instances:id:scale:", 

483 }, 

484 "terminate": { 

485 "METHODS": ("POST",), 

486 "ROLE_PERMISSION": "ns_instances:id:terminate:", 

487 }, 

488 "instantiate": { 

489 "METHODS": ("POST",), 

490 "ROLE_PERMISSION": "ns_instances:id:instantiate:", 

491 }, 

492 "migrate": { 

493 "METHODS": ("POST",), 

494 "ROLE_PERMISSION": "ns_instances:id:migrate:", 

495 }, 

496 "action": { 

497 "METHODS": ("POST",), 

498 "ROLE_PERMISSION": "ns_instances:id:action:", 

499 }, 

500 "update": { 

501 "METHODS": ("POST",), 

502 "ROLE_PERMISSION": "ns_instances:id:update:", 

503 }, 

504 }, 

505 }, 

506 "ns_lcm_op_occs": { 

507 "METHODS": ("GET",), 

508 "ROLE_PERMISSION": "ns_instances:opps:", 

509 "<ID>": { 

510 "METHODS": ("GET",), 

511 "ROLE_PERMISSION": "ns_instances:opps:id:", 

512 "cancel": { 

513 "METHODS": ("POST",), 

514 "ROLE_PERMISSION": "ns_instances:opps:cancel:", 

515 }, 

516 }, 

517 }, 

518 "vnfrs": { 

519 "METHODS": ("GET",), 

520 "ROLE_PERMISSION": "vnf_instances:", 

521 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"}, 

522 }, 

523 "vnf_instances": { 

524 "METHODS": ("GET",), 

525 "ROLE_PERMISSION": "vnf_instances:", 

526 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"}, 

527 }, 

528 "subscriptions": { 

529 "METHODS": ("GET", "POST"), 

530 "ROLE_PERMISSION": "ns_subscriptions:", 

531 "<ID>": { 

532 "METHODS": ("GET", "DELETE"), 

533 "ROLE_PERMISSION": "ns_subscriptions:id:", 

534 }, 

535 }, 

536 } 

537 }, 

538 "vnflcm": { 

539 "v1": { 

540 "vnf_instances": { 

541 "METHODS": ("GET", "POST"), 

542 "ROLE_PERMISSION": "vnflcm_instances:", 

543 "<ID>": { 

544 "METHODS": ("GET", "DELETE"), 

545 "ROLE_PERMISSION": "vnflcm_instances:id:", 

546 "scale": { 

547 "METHODS": ("POST",), 

548 "ROLE_PERMISSION": "vnflcm_instances:id:scale:", 

549 }, 

550 "terminate": { 

551 "METHODS": ("POST",), 

552 "ROLE_PERMISSION": "vnflcm_instances:id:terminate:", 

553 }, 

554 "instantiate": { 

555 "METHODS": ("POST",), 

556 "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:", 

557 }, 

558 }, 

559 }, 

560 "vnf_lcm_op_occs": { 

561 "METHODS": ("GET",), 

562 "ROLE_PERMISSION": "vnf_instances:opps:", 

563 "<ID>": { 

564 "METHODS": ("GET",), 

565 "ROLE_PERMISSION": "vnf_instances:opps:id:", 

566 }, 

567 }, 

568 "subscriptions": { 

569 "METHODS": ("GET", "POST"), 

570 "ROLE_PERMISSION": "vnflcm_subscriptions:", 

571 "<ID>": { 

572 "METHODS": ("GET", "DELETE"), 

573 "ROLE_PERMISSION": "vnflcm_subscriptions:id:", 

574 }, 

575 }, 

576 } 

577 }, 

578 "nst": { 

579 "v1": { 

580 "netslice_templates_content": { 

581 "METHODS": ("GET", "POST"), 

582 "ROLE_PERMISSION": "slice_templates:", 

583 "<ID>": { 

584 "METHODS": ("GET", "PUT", "DELETE"), 

585 "ROLE_PERMISSION": "slice_templates:id:", 

586 }, 

587 }, 

588 "netslice_templates": { 

589 "METHODS": ("GET", "POST"), 

590 "ROLE_PERMISSION": "slice_templates:", 

591 "<ID>": { 

592 "METHODS": ("GET", "DELETE"), 

593 "TODO": ("PATCH",), 

594 "ROLE_PERMISSION": "slice_templates:id:", 

595 "nst_content": { 

596 "METHODS": ("GET", "PUT"), 

597 "ROLE_PERMISSION": "slice_templates:id:content:", 

598 }, 

599 "nst": { 

600 "METHODS": ("GET",), # descriptor inside package 

601 "ROLE_PERMISSION": "slice_templates:id:content:", 

602 }, 

603 "artifacts": { 

604 "METHODS": ("GET",), 

605 "ROLE_PERMISSION": "slice_templates:id:content:", 

606 "*": None, 

607 }, 

608 }, 

609 }, 

610 "subscriptions": { 

611 "TODO": ("GET", "POST"), 

612 "<ID>": {"TODO": ("GET", "DELETE")}, 

613 }, 

614 } 

615 }, 

616 "nsilcm": { 

617 "v1": { 

618 "netslice_instances_content": { 

619 "METHODS": ("GET", "POST"), 

620 "ROLE_PERMISSION": "slice_instances:", 

621 "<ID>": { 

622 "METHODS": ("GET", "DELETE"), 

623 "ROLE_PERMISSION": "slice_instances:id:", 

624 }, 

625 }, 

626 "netslice_instances": { 

627 "METHODS": ("GET", "POST"), 

628 "ROLE_PERMISSION": "slice_instances:", 

629 "<ID>": { 

630 "METHODS": ("GET", "DELETE"), 

631 "ROLE_PERMISSION": "slice_instances:id:", 

632 "terminate": { 

633 "METHODS": ("POST",), 

634 "ROLE_PERMISSION": "slice_instances:id:terminate:", 

635 }, 

636 "instantiate": { 

637 "METHODS": ("POST",), 

638 "ROLE_PERMISSION": "slice_instances:id:instantiate:", 

639 }, 

640 "action": { 

641 "METHODS": ("POST",), 

642 "ROLE_PERMISSION": "slice_instances:id:action:", 

643 }, 

644 }, 

645 }, 

646 "nsi_lcm_op_occs": { 

647 "METHODS": ("GET",), 

648 "ROLE_PERMISSION": "slice_instances:opps:", 

649 "<ID>": { 

650 "METHODS": ("GET",), 

651 "ROLE_PERMISSION": "slice_instances:opps:id:", 

652 }, 

653 }, 

654 } 

655 }, 

656 "nspm": { 

657 "v1": { 

658 "pm_jobs": { 

659 "<ID>": { 

660 "reports": { 

661 "<ID>": { 

662 "METHODS": ("GET",), 

663 "ROLE_PERMISSION": "reports:id:", 

664 } 

665 } 

666 }, 

667 }, 

668 }, 

669 }, 

670 "nsfm": { 

671 "v1": { 

672 "alarms": { 

673 "METHODS": ("GET", "PATCH"), 

674 "ROLE_PERMISSION": "alarms:", 

675 "<ID>": { 

676 "METHODS": ("GET", "PATCH"), 

677 "ROLE_PERMISSION": "alarms:id:", 

678 }, 

679 } 

680 }, 

681 }, 

682 "k8scluster": { 

683 "v1": { 

684 "clusters": { 

685 "METHODS": ("GET", "POST"), 

686 "ROLE_PERMISSION": "k8scluster:", 

687 "<ID>": { 

688 "METHODS": ("GET", "PATCH", "DELETE"), 

689 "ROLE_PERMISSION": "k8scluster:id:", 

690 "app_profiles": { 

691 "METHODS": ("PATCH", "GET"), 

692 "ROLE_PERMISSION": "k8scluster:id:app_profiles:", 

693 }, 

694 "infra_controller_profiles": { 

695 "METHODS": ("PATCH", "GET"), 

696 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:", 

697 }, 

698 "infra_config_profiles": { 

699 "METHODS": ("PATCH", "GET"), 

700 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:", 

701 }, 

702 "resource_profiles": { 

703 "METHODS": ("PATCH", "GET"), 

704 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:", 

705 }, 

706 "deregister": { 

707 "METHODS": ("DELETE",), 

708 "ROLE_PERMISSION": "k8scluster:id:deregister:", 

709 }, 

710 "get_creds": { 

711 "METHODS": ("GET",), 

712 "ROLE_PERMISSION": "k8scluster:id:get_creds:", 

713 }, 

714 "get_creds_file": { 

715 "METHODS": ("GET",), 

716 "ROLE_PERMISSION": "k8scluster:id:get_creds_file:", 

717 "<ID>": { 

718 "METHODS": ("GET",), 

719 "ROLE_PERMISSION": "k8scluster:id:get_creds_file:id", 

720 }, 

721 }, 

722 "update": { 

723 "METHODS": ("POST",), 

724 "ROLE_PERMISSION": "k8scluster:id:update:", 

725 }, 

726 "scale": { 

727 "METHODS": ("POST",), 

728 "ROLE_PERMISSION": "k8scluster:id:scale:", 

729 }, 

730 "upgrade": { 

731 "METHODS": ("POST",), 

732 "ROLE_PERMISSION": "k8scluster:id:upgrade:", 

733 }, 

734 }, 

735 "register": { 

736 "METHODS": ("POST",), 

737 "ROLE_PERMISSION": "k8scluster:register:", 

738 }, 

739 }, 

740 "app_profiles": { 

741 "METHODS": ("POST", "GET"), 

742 "ROLE_PERMISSION": "k8scluster:app_profiles:", 

743 "<ID>": { 

744 "METHODS": ("GET", "PATCH", "DELETE"), 

745 "ROLE_PERMISSION": "k8scluster:app_profiles:id:", 

746 }, 

747 }, 

748 "infra_controller_profiles": { 

749 "METHODS": ("POST", "GET"), 

750 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:", 

751 "<ID>": { 

752 "METHODS": ("GET", "PATCH", "DELETE"), 

753 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:id:", 

754 }, 

755 }, 

756 "infra_config_profiles": { 

757 "METHODS": ("POST", "GET"), 

758 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:", 

759 "<ID>": { 

760 "METHODS": ("GET", "PATCH", "DELETE"), 

761 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:id:", 

762 }, 

763 }, 

764 "resource_profiles": { 

765 "METHODS": ("POST", "GET"), 

766 "ROLE_PERMISSION": "k8scluster:resource_profiles:", 

767 "<ID>": { 

768 "METHODS": ("GET", "PATCH", "DELETE"), 

769 "ROLE_PERMISSION": "k8scluster:resource_profiles:id:", 

770 }, 

771 }, 

772 } 

773 }, 

774 "ksu": { 

775 "v1": { 

776 "ksus": { 

777 "METHODS": ("GET", "POST"), 

778 "ROLE_PERMISSION": "ksu:", 

779 "<ID>": { 

780 "METHODS": ("GET", "PATCH", "DELETE"), 

781 "ROLE_PERMISSION": "ksu:id:", 

782 "clone": { 

783 "METHODS": ("POST",), 

784 "ROLE_PERMISSION": "ksu:id:clone:", 

785 }, 

786 "move": { 

787 "METHODS": ("POST",), 

788 "ROLE_PERMISSION": "ksu:id:move:", 

789 }, 

790 }, 

791 "update": { 

792 "METHODS": ("POST",), 

793 "ROLE_PERMISSION": "ksu:", 

794 }, 

795 "delete": { 

796 "METHODS": ("POST",), 

797 "ROLE_PERMISSION": "ksu:", 

798 }, 

799 }, 

800 } 

801 }, 

802 "oka": { 

803 "v1": { 

804 "oka_packages": { 

805 "METHODS": ("GET", "POST"), 

806 "ROLE_PERMISSION": "oka_pkg:", 

807 "<ID>": { 

808 "METHODS": ("GET", "PATCH", "DELETE", "PUT"), 

809 "ROLE_PERMISSION": "oka_pkg:id:", 

810 }, 

811 } 

812 } 

813 }, 

814} 

815 

816 

817class NbiException(Exception): 

818 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED): 

819 Exception.__init__(self, message) 

820 self.http_code = http_code 

821 

822 

823class Server(object): 

824 instance = 0 

825 # to decode bytes to str 

826 reader = getreader("utf-8") 

827 

828 def __init__(self): 

829 self.instance += 1 

830 self.authenticator = Authenticator(valid_url_methods, valid_query_string) 

831 self.engine = Engine(self.authenticator) 

832 self.logger = logging.getLogger("nbi.server") 

833 

834 def _format_in(self, kwargs): 

835 error_text = "" # error_text must be initialized outside try 

836 try: 

837 indata = None 

838 if cherrypy.request.body.length: 

839 error_text = "Invalid input format " 

840 

841 if "Content-Type" in cherrypy.request.headers: 

842 if "application/json" in cherrypy.request.headers["Content-Type"]: 

843 error_text = "Invalid json format " 

844 indata = json.load(self.reader(cherrypy.request.body)) 

845 cherrypy.request.headers.pop("Content-File-MD5", None) 

846 elif "application/yaml" in cherrypy.request.headers["Content-Type"]: 

847 error_text = "Invalid yaml format " 

848 indata = yaml.safe_load(cherrypy.request.body) 

849 cherrypy.request.headers.pop("Content-File-MD5", None) 

850 elif ( 

851 "application/binary" in cherrypy.request.headers["Content-Type"] 

852 or "application/gzip" 

853 in cherrypy.request.headers["Content-Type"] 

854 or "application/zip" in cherrypy.request.headers["Content-Type"] 

855 or "text/plain" in cherrypy.request.headers["Content-Type"] 

856 ): 

857 indata = cherrypy.request.body # .read() 

858 elif ( 

859 "multipart/form-data" 

860 in cherrypy.request.headers["Content-Type"] 

861 ): 

862 if ( 

863 "descriptor_file" in kwargs 

864 or "package" in kwargs 

865 and "name" in kwargs 

866 ): 

867 filecontent = "" 

868 if "descriptor_file" in kwargs: 

869 filecontent = kwargs.pop("descriptor_file") 

870 if "package" in kwargs: 

871 filecontent = kwargs.pop("package") 

872 if not filecontent.file: 

873 raise NbiException( 

874 "empty file or content", HTTPStatus.BAD_REQUEST 

875 ) 

876 indata = filecontent 

877 if filecontent.content_type.value: 

878 cherrypy.request.headers[ 

879 "Content-Type" 

880 ] = filecontent.content_type.value 

881 elif "package" in kwargs: 

882 filecontent = kwargs.pop("package") 

883 if not filecontent.file: 

884 raise NbiException( 

885 "empty file or content", HTTPStatus.BAD_REQUEST 

886 ) 

887 indata = filecontent 

888 if filecontent.content_type.value: 

889 cherrypy.request.headers[ 

890 "Content-Type" 

891 ] = filecontent.content_type.value 

892 else: 

893 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable, 

894 # "Only 'Content-Type' of type 'application/json' or 

895 # 'application/yaml' for input format are available") 

896 error_text = "Invalid yaml format " 

897 indata = yaml.safe_load(cherrypy.request.body) 

898 cherrypy.request.headers.pop("Content-File-MD5", None) 

899 else: 

900 error_text = "Invalid yaml format " 

901 indata = yaml.safe_load(cherrypy.request.body) 

902 cherrypy.request.headers.pop("Content-File-MD5", None) 

903 if not indata: 

904 indata = {} 

905 format_yaml = False 

906 if cherrypy.request.headers.get("Query-String-Format") == "yaml": 

907 format_yaml = True 

908 

909 for k, v in kwargs.items(): 

910 if isinstance(v, str): 

911 if v == "": 

912 kwargs[k] = None 

913 elif format_yaml: 

914 try: 

915 kwargs[k] = yaml.safe_load(v) 

916 except Exception: 

917 pass 

918 elif ( 

919 k.endswith(".gt") 

920 or k.endswith(".lt") 

921 or k.endswith(".gte") 

922 or k.endswith(".lte") 

923 ): 

924 try: 

925 kwargs[k] = int(v) 

926 except Exception: 

927 try: 

928 kwargs[k] = float(v) 

929 except Exception: 

930 pass 

931 elif v.find(",") > 0: 

932 kwargs[k] = v.split(",") 

933 elif isinstance(v, (list, tuple)): 

934 for index in range(0, len(v)): 

935 if v[index] == "": 

936 v[index] = None 

937 elif format_yaml: 

938 try: 

939 v[index] = yaml.safe_load(v[index]) 

940 except Exception: 

941 pass 

942 

943 return indata 

944 except (ValueError, yaml.YAMLError) as exc: 

945 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST) 

946 except KeyError as exc: 

947 raise NbiException( 

948 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST 

949 ) 

950 except Exception as exc: 

951 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST) 

952 

953 @staticmethod 

954 def _format_out(data, token_info=None, _format=None): 

955 """ 

956 return string of dictionary data according to requested json, yaml, xml. By default json 

957 :param data: response to be sent. Can be a dict, text or file 

958 :param token_info: Contains among other username and project 

959 :param _format: The format to be set as Content-Type if data is a file 

960 :return: None 

961 """ 

962 accept = cherrypy.request.headers.get("Accept") 

963 if data is None: 

964 if accept and "text/html" in accept: 

965 return html.format( 

966 data, cherrypy.request, cherrypy.response, token_info 

967 ) 

968 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value 

969 return 

970 elif hasattr(data, "read"): # file object 

971 if _format: 

972 cherrypy.response.headers["Content-Type"] = _format 

973 elif "b" in data.mode: # binariy asssumig zip 

974 cherrypy.response.headers["Content-Type"] = "application/zip" 

975 else: 

976 cherrypy.response.headers["Content-Type"] = "text/plain" 

977 # TODO check that cherrypy close file. If not implement pending things to close per thread next 

978 return data 

979 if accept: 

980 if "text/html" in accept: 

981 return html.format( 

982 data, cherrypy.request, cherrypy.response, token_info 

983 ) 

984 elif "application/yaml" in accept or "*/*" in accept: 

985 pass 

986 elif "application/json" in accept or ( 

987 cherrypy.response.status and cherrypy.response.status >= 300 

988 ): 

989 cherrypy.response.headers[ 

990 "Content-Type" 

991 ] = "application/json; charset=utf-8" 

992 a = json.dumps(data, indent=4) + "\n" 

993 return a.encode("utf8") 

994 cherrypy.response.headers["Content-Type"] = "application/yaml" 

995 return yaml.safe_dump( 

996 data, 

997 explicit_start=True, 

998 indent=4, 

999 default_flow_style=False, 

1000 tags=False, 

1001 encoding="utf-8", 

1002 allow_unicode=True, 

1003 ) # , canonical=True, default_style='"' 

1004 

1005 @cherrypy.expose 

1006 def index(self, *args, **kwargs): 

1007 token_info = None 

1008 try: 

1009 if cherrypy.request.method == "GET": 

1010 token_info = self.authenticator.authorize() 

1011 outdata = token_info # Home page 

1012 else: 

1013 raise cherrypy.HTTPError( 

1014 HTTPStatus.METHOD_NOT_ALLOWED.value, 

1015 "Method {} not allowed for tokens".format(cherrypy.request.method), 

1016 ) 

1017 

1018 return self._format_out(outdata, token_info) 

1019 

1020 except (EngineException, AuthException) as e: 

1021 # cherrypy.log("index Exception {}".format(e)) 

1022 cherrypy.response.status = e.http_code.value 

1023 return self._format_out("Welcome to OSM!", token_info) 

1024 

1025 @cherrypy.expose 

1026 def version(self, *args, **kwargs): 

1027 # TODO consider to remove and provide version using the static version file 

1028 try: 

1029 if cherrypy.request.method != "GET": 

1030 raise NbiException( 

1031 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED 

1032 ) 

1033 elif args or kwargs: 

1034 raise NbiException( 

1035 "Invalid URL or query string for version", 

1036 HTTPStatus.METHOD_NOT_ALLOWED, 

1037 ) 

1038 # TODO include version of other modules, pick up from some kafka admin message 

1039 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date} 

1040 return self._format_out(osm_nbi_version) 

1041 except NbiException as e: 

1042 cherrypy.response.status = e.http_code.value 

1043 problem_details = { 

1044 "code": e.http_code.name, 

1045 "status": e.http_code.value, 

1046 "detail": str(e), 

1047 } 

1048 return self._format_out(problem_details, None) 

1049 

1050 def domain(self): 

1051 try: 

1052 domains = { 

1053 "user_domain_name": cherrypy.tree.apps["/osm"] 

1054 .config["authentication"] 

1055 .get("user_domain_name"), 

1056 "project_domain_name": cherrypy.tree.apps["/osm"] 

1057 .config["authentication"] 

1058 .get("project_domain_name"), 

1059 } 

1060 return self._format_out(domains) 

1061 except NbiException as e: 

1062 cherrypy.response.status = e.http_code.value 

1063 problem_details = { 

1064 "code": e.http_code.name, 

1065 "status": e.http_code.value, 

1066 "detail": str(e), 

1067 } 

1068 return self._format_out(problem_details, None) 

1069 

1070 @staticmethod 

1071 def _format_login(token_info): 

1072 """ 

1073 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will 

1074 log this information 

1075 :param token_info: Dictionary with token content 

1076 :return: None 

1077 """ 

1078 cherrypy.request.login = token_info.get("username", "-") 

1079 if token_info.get("project_name"): 

1080 cherrypy.request.login += "/" + token_info["project_name"] 

1081 if token_info.get("id"): 

1082 cherrypy.request.login += ";session=" + token_info["id"][0:12] 

1083 

1084 # NS Fault Management 

1085 @cherrypy.expose 

1086 def nsfm( 

1087 self, 

1088 version=None, 

1089 topic=None, 

1090 uuid=None, 

1091 project_name=None, 

1092 ns_id=None, 

1093 *args, 

1094 **kwargs 

1095 ): 

1096 if topic == "alarms": 

1097 try: 

1098 method = cherrypy.request.method 

1099 role_permission = self._check_valid_url_method( 

1100 method, "nsfm", version, topic, None, None, *args 

1101 ) 

1102 query_string_operations = self._extract_query_string_operations( 

1103 kwargs, method 

1104 ) 

1105 

1106 self.authenticator.authorize( 

1107 role_permission, query_string_operations, None 

1108 ) 

1109 

1110 # to handle get request 

1111 if cherrypy.request.method == "GET": 

1112 # if request is on basis of uuid 

1113 if uuid and uuid != "None": 

1114 try: 

1115 alarm = self.engine.db.get_one("alarms", {"uuid": uuid}) 

1116 alarm_action = self.engine.db.get_one( 

1117 "alarms_action", {"uuid": uuid} 

1118 ) 

1119 alarm.update(alarm_action) 

1120 vnf = self.engine.db.get_one( 

1121 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]} 

1122 ) 

1123 alarm["vnf-id"] = vnf["_id"] 

1124 return self._format_out(str(alarm)) 

1125 except Exception: 

1126 return self._format_out("Please provide valid alarm uuid") 

1127 elif ns_id and ns_id != "None": 

1128 # if request is on basis of ns_id 

1129 try: 

1130 alarms = self.engine.db.get_list( 

1131 "alarms", {"tags.ns_id": ns_id} 

1132 ) 

1133 for alarm in alarms: 

1134 alarm_action = self.engine.db.get_one( 

1135 "alarms_action", {"uuid": alarm["uuid"]} 

1136 ) 

1137 alarm.update(alarm_action) 

1138 return self._format_out(str(alarms)) 

1139 except Exception: 

1140 return self._format_out("Please provide valid ns id") 

1141 else: 

1142 # to return only alarm which are related to given project 

1143 project = self.engine.db.get_one( 

1144 "projects", {"name": project_name} 

1145 ) 

1146 project_id = project.get("_id") 

1147 ns_list = self.engine.db.get_list( 

1148 "nsrs", {"_admin.projects_read": project_id} 

1149 ) 

1150 ns_ids = [] 

1151 for ns in ns_list: 

1152 ns_ids.append(ns.get("_id")) 

1153 alarms = self.engine.db.get_list("alarms") 

1154 alarm_list = [ 

1155 alarm 

1156 for alarm in alarms 

1157 if alarm["tags"]["ns_id"] in ns_ids 

1158 ] 

1159 for alrm in alarm_list: 

1160 action = self.engine.db.get_one( 

1161 "alarms_action", {"uuid": alrm.get("uuid")} 

1162 ) 

1163 alrm.update(action) 

1164 return self._format_out(str(alarm_list)) 

1165 # to handle patch request for alarm update 

1166 elif cherrypy.request.method == "PATCH": 

1167 data = yaml.safe_load(cherrypy.request.body) 

1168 try: 

1169 # check if uuid is valid 

1170 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")}) 

1171 except Exception: 

1172 return self._format_out("Please provide valid alarm uuid.") 

1173 if data.get("is_enable") is not None: 

1174 if data.get("is_enable"): 

1175 alarm_status = "ok" 

1176 else: 

1177 alarm_status = "disabled" 

1178 self.engine.db.set_one( 

1179 "alarms", 

1180 {"uuid": data.get("uuid")}, 

1181 {"alarm_status": alarm_status}, 

1182 ) 

1183 else: 

1184 self.engine.db.set_one( 

1185 "alarms", 

1186 {"uuid": data.get("uuid")}, 

1187 {"threshold": data.get("threshold")}, 

1188 ) 

1189 return self._format_out("Alarm updated") 

1190 except Exception as e: 

1191 if isinstance( 

1192 e, 

1193 ( 

1194 NbiException, 

1195 EngineException, 

1196 DbException, 

1197 FsException, 

1198 MsgException, 

1199 AuthException, 

1200 ValidationError, 

1201 AuthconnException, 

1202 ), 

1203 ): 

1204 http_code_value = cherrypy.response.status = e.http_code.value 

1205 http_code_name = e.http_code.name 

1206 cherrypy.log("Exception {}".format(e)) 

1207 else: 

1208 http_code_value = ( 

1209 cherrypy.response.status 

1210 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR 

1211 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True) 

1212 http_code_name = HTTPStatus.BAD_REQUEST.name 

1213 problem_details = { 

1214 "code": http_code_name, 

1215 "status": http_code_value, 

1216 "detail": str(e), 

1217 } 

1218 return self._format_out(problem_details) 

1219 

1220 @cherrypy.expose 

1221 def token(self, method, token_id=None, kwargs=None): 

1222 token_info = None 

1223 # self.engine.load_dbase(cherrypy.request.app.config) 

1224 indata = self._format_in(kwargs) 

1225 if not isinstance(indata, dict): 

1226 raise NbiException( 

1227 "Expected application/yaml or application/json Content-Type", 

1228 HTTPStatus.BAD_REQUEST, 

1229 ) 

1230 

1231 if method == "GET": 

1232 token_info = self.authenticator.authorize() 

1233 # for logging 

1234 self._format_login(token_info) 

1235 if token_id: 

1236 outdata = self.authenticator.get_token(token_info, token_id) 

1237 else: 

1238 outdata = self.authenticator.get_token_list(token_info) 

1239 elif method == "POST": 

1240 try: 

1241 token_info = self.authenticator.authorize() 

1242 except Exception: 

1243 token_info = None 

1244 if kwargs: 

1245 indata.update(kwargs) 

1246 # This is needed to log the user when authentication fails 

1247 cherrypy.request.login = "{}".format(indata.get("username", "-")) 

1248 outdata = token_info = self.authenticator.new_token( 

1249 token_info, indata, cherrypy.request.remote 

1250 ) 

1251 if outdata.get("email") or outdata.get("otp") == "invalid": 

1252 return self._format_out(outdata, token_info) 

1253 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101 

1254 self._set_location_header("admin", "v1", "tokens", outdata["_id"]) 

1255 # for logging 

1256 self._format_login(token_info) 

1257 if outdata.get("otp") == "valid": 

1258 outdata = { 

1259 "id": outdata["id"], 

1260 "message": "valid_otp", 

1261 "user_id": outdata["user_id"], 

1262 } 

1263 # password expiry check 

1264 elif self.authenticator.check_password_expiry(outdata): 

1265 outdata = { 

1266 "id": outdata["id"], 

1267 "message": "change_password", 

1268 "user_id": outdata["user_id"], 

1269 } 

1270 # cherrypy.response.cookie["Authorization"] = outdata["id"] 

1271 # cherrypy.response.cookie["Authorization"]['expires'] = 3600 

1272 cef_event( 

1273 cef_logger, 

1274 { 

1275 "name": "User Login", 

1276 "sourceUserName": token_info.get("username"), 

1277 "message": "User Logged In, Project={} Outcome=Success".format( 

1278 token_info.get("project_name") 

1279 ), 

1280 }, 

1281 ) 

1282 cherrypy.log("{}".format(cef_logger)) 

1283 elif method == "DELETE": 

1284 if not token_id and "id" in kwargs: 

1285 token_id = kwargs["id"] 

1286 elif not token_id: 

1287 token_info = self.authenticator.authorize() 

1288 # for logging 

1289 self._format_login(token_info) 

1290 token_id = token_info["_id"] 

1291 if current_backend != "keystone": 

1292 token_details = self.engine.db.get_one("tokens", {"_id": token_id}) 

1293 current_user = token_details.get("username") 

1294 current_project = token_details.get("project_name") 

1295 else: 

1296 current_user = "keystone backend" 

1297 current_project = "keystone backend" 

1298 outdata = self.authenticator.del_token(token_id) 

1299 token_info = None 

1300 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101 

1301 cef_event( 

1302 cef_logger, 

1303 { 

1304 "name": "User Logout", 

1305 "sourceUserName": current_user, 

1306 "message": "User Logged Out, Project={} Outcome=Success".format( 

1307 current_project 

1308 ), 

1309 }, 

1310 ) 

1311 cherrypy.log("{}".format(cef_logger)) 

1312 # cherrypy.response.cookie["Authorization"] = token_id 

1313 # cherrypy.response.cookie["Authorization"]['expires'] = 0 

1314 else: 

1315 raise NbiException( 

1316 "Method {} not allowed for token".format(method), 

1317 HTTPStatus.METHOD_NOT_ALLOWED, 

1318 ) 

1319 return self._format_out(outdata, token_info) 

1320 

1321 @cherrypy.expose 

1322 def test(self, *args, **kwargs): 

1323 if not cherrypy.config.get("server.enable_test") or ( 

1324 isinstance(cherrypy.config["server.enable_test"], str) 

1325 and cherrypy.config["server.enable_test"].lower() == "false" 

1326 ): 

1327 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value 

1328 return "test URL is disabled" 

1329 thread_info = None 

1330 if args and args[0] == "help": 

1331 return ( 

1332 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n" 

1333 "sleep/<time>\nmessage/topic\n</pre></html>" 

1334 ) 

1335 

1336 elif args and args[0] == "init": 

1337 try: 

1338 # self.engine.load_dbase(cherrypy.request.app.config) 

1339 pid = self.authenticator.create_admin_project() 

1340 self.authenticator.create_admin_user(pid) 

1341 return "Done. User 'admin', password 'admin' created" 

1342 except Exception: 

1343 cherrypy.response.status = HTTPStatus.FORBIDDEN.value 

1344 return self._format_out("Database already initialized") 

1345 elif args and args[0] == "file": 

1346 return cherrypy.lib.static.serve_file( 

1347 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1], 

1348 "text/plain", 

1349 "attachment", 

1350 ) 

1351 elif args and args[0] == "file2": 

1352 f_path = ( 

1353 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1] 

1354 ) 

1355 f = open(f_path, "r") 

1356 cherrypy.response.headers["Content-type"] = "text/plain" 

1357 return f 

1358 

1359 elif len(args) == 2 and args[0] == "db-clear": 

1360 deleted_info = self.engine.db.del_list(args[1], kwargs) 

1361 return "{} {} deleted\n".format(deleted_info["deleted"], args[1]) 

1362 elif len(args) and args[0] == "fs-clear": 

1363 if len(args) >= 2: 

1364 folders = (args[1],) 

1365 else: 

1366 folders = self.engine.fs.dir_ls(".") 

1367 for folder in folders: 

1368 self.engine.fs.file_delete(folder) 

1369 return ",".join(folders) + " folders deleted\n" 

1370 elif args and args[0] == "login": 

1371 if not cherrypy.request.headers.get("Authorization"): 

1372 cherrypy.response.headers[ 

1373 "WWW-Authenticate" 

1374 ] = 'Basic realm="Access to OSM site", charset="UTF-8"' 

1375 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value 

1376 elif args and args[0] == "login2": 

1377 if not cherrypy.request.headers.get("Authorization"): 

1378 cherrypy.response.headers[ 

1379 "WWW-Authenticate" 

1380 ] = 'Bearer realm="Access to OSM site"' 

1381 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value 

1382 elif args and args[0] == "sleep": 

1383 sleep_time = 5 

1384 try: 

1385 sleep_time = int(args[1]) 

1386 except Exception: 

1387 cherrypy.response.status = HTTPStatus.FORBIDDEN.value 

1388 return self._format_out("Database already initialized") 

1389 thread_info = cherrypy.thread_data 

1390 print(thread_info) 

1391 time.sleep(sleep_time) 

1392 # thread_info 

1393 elif len(args) >= 2 and args[0] == "message": 

1394 main_topic = args[1] 

1395 return_text = "<html><pre>{} ->\n".format(main_topic) 

1396 try: 

1397 if cherrypy.request.method == "POST": 

1398 to_send = yaml.safe_load(cherrypy.request.body) 

1399 for k, v in to_send.items(): 

1400 self.engine.msg.write(main_topic, k, v) 

1401 return_text += " {}: {}\n".format(k, v) 

1402 elif cherrypy.request.method == "GET": 

1403 for k, v in kwargs.items(): 

1404 v_dict = yaml.safe_load(v) 

1405 self.engine.msg.write(main_topic, k, v_dict) 

1406 return_text += " {}: {}\n".format(k, v_dict) 

1407 except Exception as e: 

1408 return_text += "Error: " + str(e) 

1409 return_text += "</pre></html>\n" 

1410 return return_text 

1411 

1412 return_text = ( 

1413 "<html><pre>\nheaders:\n args: {}\n".format(args) 

1414 + " kwargs: {}\n".format(kwargs) 

1415 + " headers: {}\n".format(cherrypy.request.headers) 

1416 + " path_info: {}\n".format(cherrypy.request.path_info) 

1417 + " query_string: {}\n".format(cherrypy.request.query_string) 

1418 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101 

1419 + " cookie: {}\n".format(cherrypy.request.cookie) 

1420 + " method: {}\n".format(cherrypy.request.method) 

1421 + " session: {}\n".format( 

1422 cherrypy.session.get("fieldname") # pylint: disable=E1101 

1423 ) 

1424 + " body:\n" 

1425 ) 

1426 return_text += " length: {}\n".format(cherrypy.request.body.length) 

1427 if cherrypy.request.body.length: 

1428 return_text += " content: {}\n".format( 

1429 str( 

1430 cherrypy.request.body.read( 

1431 int(cherrypy.request.headers.get("Content-Length", 0)) 

1432 ) 

1433 ) 

1434 ) 

1435 if thread_info: 

1436 return_text += "thread: {}\n".format(thread_info) 

1437 return_text += "</pre></html>" 

1438 return return_text 

1439 

1440 @staticmethod 

1441 def _check_valid_url_method(method, *args): 

1442 if len(args) < 3: 

1443 raise NbiException( 

1444 "URL must contain at least 'main_topic/version/topic'", 

1445 HTTPStatus.METHOD_NOT_ALLOWED, 

1446 ) 

1447 

1448 reference = valid_url_methods 

1449 for arg in args: 

1450 if arg is None: 

1451 break 

1452 if not isinstance(reference, dict): 

1453 raise NbiException( 

1454 "URL contains unexpected extra items '{}'".format(arg), 

1455 HTTPStatus.METHOD_NOT_ALLOWED, 

1456 ) 

1457 

1458 if arg in reference: 

1459 reference = reference[arg] 

1460 elif "<ID>" in reference: 

1461 reference = reference["<ID>"] 

1462 elif "*" in reference: 

1463 # if there is content 

1464 if reference["*"]: 

1465 reference = reference["*"] 

1466 break 

1467 else: 

1468 raise NbiException( 

1469 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED 

1470 ) 

1471 if "TODO" in reference and method in reference["TODO"]: 

1472 raise NbiException( 

1473 "Method {} not supported yet for this URL".format(method), 

1474 HTTPStatus.NOT_IMPLEMENTED, 

1475 ) 

1476 elif "METHODS" in reference and method not in reference["METHODS"]: 

1477 raise NbiException( 

1478 "Method {} not supported for this URL".format(method), 

1479 HTTPStatus.METHOD_NOT_ALLOWED, 

1480 ) 

1481 return reference["ROLE_PERMISSION"] + method.lower() 

1482 

1483 @staticmethod 

1484 def _set_location_header(main_topic, version, topic, id): 

1485 """ 

1486 Insert response header Location with the URL of created item base on URL params 

1487 :param main_topic: 

1488 :param version: 

1489 :param topic: 

1490 :param id: 

1491 :return: None 

1492 """ 

1493 # Use cherrypy.request.base for absoluted path and make use of request.header HOST just in case behind aNAT 

1494 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format( 

1495 main_topic, version, topic, id 

1496 ) 

1497 return 

1498 

1499 @staticmethod 

1500 def _extract_query_string_operations(kwargs, method): 

1501 """ 

1502 

1503 :param kwargs: 

1504 :return: 

1505 """ 

1506 query_string_operations = [] 

1507 if kwargs: 

1508 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"): 

1509 if qs in kwargs and kwargs[qs].lower() != "false": 

1510 query_string_operations.append(qs.lower() + ":" + method.lower()) 

1511 return query_string_operations 

1512 

1513 @staticmethod 

1514 def _manage_admin_query(token_info, kwargs, method, _id): 

1515 """ 

1516 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT 

1517 Check that users has rights to use them and returs the admin_query 

1518 :param token_info: token_info rights obtained by token 

1519 :param kwargs: query string input. 

1520 :param method: http method: GET, POSST, PUT, ... 

1521 :param _id: 

1522 :return: admin_query dictionary with keys: 

1523 public: True, False or None 

1524 force: True or False 

1525 project_id: tuple with projects used for accessing an element 

1526 set_project: tuple with projects that a created element will belong to 

1527 method: show, list, delete, write 

1528 """ 

1529 admin_query = { 

1530 "force": False, 

1531 "project_id": (token_info["project_id"],), 

1532 "username": token_info["username"], 

1533 "user_id": token_info["user_id"], 

1534 "admin": token_info["admin"], 

1535 "admin_show": token_info["admin_show"], 

1536 "public": None, 

1537 "allow_show_user_project_role": token_info["allow_show_user_project_role"], 

1538 } 

1539 if kwargs: 

1540 # FORCE 

1541 if "FORCE" in kwargs: 

1542 if ( 

1543 kwargs["FORCE"].lower() != "false" 

1544 ): # if None or True set force to True 

1545 admin_query["force"] = True 

1546 del kwargs["FORCE"] 

1547 # PUBLIC 

1548 if "PUBLIC" in kwargs: 

1549 if ( 

1550 kwargs["PUBLIC"].lower() != "false" 

1551 ): # if None or True set public to True 

1552 admin_query["public"] = True 

1553 else: 

1554 admin_query["public"] = False 

1555 del kwargs["PUBLIC"] 

1556 # ADMIN 

1557 if "ADMIN" in kwargs: 

1558 behave_as = kwargs.pop("ADMIN") 

1559 if behave_as.lower() != "false": 

1560 if not token_info["admin"]: 

1561 raise NbiException( 

1562 "Only admin projects can use 'ADMIN' query string", 

1563 HTTPStatus.UNAUTHORIZED, 

1564 ) 

1565 if ( 

1566 not behave_as or behave_as.lower() == "true" 

1567 ): # convert True, None to empty list 

1568 admin_query["project_id"] = () 

1569 elif isinstance(behave_as, (list, tuple)): 

1570 admin_query["project_id"] = behave_as 

1571 else: # isinstance(behave_as, str) 

1572 admin_query["project_id"] = (behave_as,) 

1573 if "SET_PROJECT" in kwargs: 

1574 set_project = kwargs.pop("SET_PROJECT") 

1575 if not set_project: 

1576 admin_query["set_project"] = list(admin_query["project_id"]) 

1577 else: 

1578 if isinstance(set_project, str): 

1579 set_project = (set_project,) 

1580 if admin_query["project_id"]: 

1581 for p in set_project: 

1582 if p not in admin_query["project_id"]: 

1583 raise NbiException( 

1584 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or " 

1585 "'ADMIN='{p}'".format(p=p), 

1586 HTTPStatus.UNAUTHORIZED, 

1587 ) 

1588 admin_query["set_project"] = set_project 

1589 

1590 # PROJECT_READ 

1591 # if "PROJECT_READ" in kwargs: 

1592 # admin_query["project"] = kwargs.pop("project") 

1593 # if admin_query["project"] == token_info["project_id"]: 

1594 if method == "GET": 

1595 if _id: 

1596 admin_query["method"] = "show" 

1597 else: 

1598 admin_query["method"] = "list" 

1599 elif method == "DELETE": 

1600 admin_query["method"] = "delete" 

1601 else: 

1602 admin_query["method"] = "write" 

1603 return admin_query 

1604 

1605 @cherrypy.expose 

1606 def default( 

1607 self, 

1608 main_topic=None, 

1609 version=None, 

1610 topic=None, 

1611 _id=None, 

1612 item=None, 

1613 *args, 

1614 **kwargs 

1615 ): 

1616 token_info = None 

1617 outdata = {} 

1618 _format = None 

1619 method = "DONE" 

1620 engine_topic = None 

1621 rollback = [] 

1622 engine_session = None 

1623 url_id = "" 

1624 log_mapping = { 

1625 "POST": "Creating", 

1626 "GET": "Fetching", 

1627 "DELETE": "Deleting", 

1628 "PUT": "Updating", 

1629 "PATCH": "Updating", 

1630 } 

1631 try: 

1632 if not main_topic or not version or not topic: 

1633 raise NbiException( 

1634 "URL must contain at least 'main_topic/version/topic'", 

1635 HTTPStatus.METHOD_NOT_ALLOWED, 

1636 ) 

1637 if main_topic not in ( 

1638 "admin", 

1639 "vnfpkgm", 

1640 "nsd", 

1641 "nslcm", 

1642 "pdu", 

1643 "nst", 

1644 "nsilcm", 

1645 "nspm", 

1646 "vnflcm", 

1647 "k8scluster", 

1648 "ksu", 

1649 "oka", 

1650 ): 

1651 raise NbiException( 

1652 "URL main_topic '{}' not supported".format(main_topic), 

1653 HTTPStatus.METHOD_NOT_ALLOWED, 

1654 ) 

1655 if version != "v1": 

1656 raise NbiException( 

1657 "URL version '{}' not supported".format(version), 

1658 HTTPStatus.METHOD_NOT_ALLOWED, 

1659 ) 

1660 if _id is not None: 

1661 url_id = _id 

1662 

1663 if ( 

1664 kwargs 

1665 and "METHOD" in kwargs 

1666 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH") 

1667 ): 

1668 method = kwargs.pop("METHOD") 

1669 else: 

1670 method = cherrypy.request.method 

1671 

1672 role_permission = self._check_valid_url_method( 

1673 method, main_topic, version, topic, _id, item, *args 

1674 ) 

1675 query_string_operations = self._extract_query_string_operations( 

1676 kwargs, method 

1677 ) 

1678 if main_topic == "admin" and topic == "tokens": 

1679 return self.token(method, _id, kwargs) 

1680 token_info = self.authenticator.authorize( 

1681 role_permission, query_string_operations, _id 

1682 ) 

1683 if main_topic == "admin" and topic == "domains": 

1684 return self.domain() 

1685 engine_session = self._manage_admin_query(token_info, kwargs, method, _id) 

1686 indata = self._format_in(kwargs) 

1687 engine_topic = topic 

1688 

1689 if item and topic != "pm_jobs": 

1690 engine_topic = item 

1691 

1692 if main_topic == "nsd": 

1693 engine_topic = "nsds" 

1694 if topic == "ns_config_template": 

1695 engine_topic = "nsconfigtemps" 

1696 elif main_topic == "vnfpkgm": 

1697 engine_topic = "vnfds" 

1698 if topic == "vnfpkg_op_occs": 

1699 engine_topic = "vnfpkgops" 

1700 if topic == "vnf_packages" and item == "action": 

1701 engine_topic = "vnfpkgops" 

1702 elif main_topic == "nslcm": 

1703 engine_topic = "nsrs" 

1704 if topic == "ns_lcm_op_occs": 

1705 engine_topic = "nslcmops" 

1706 if topic == "vnfrs" or topic == "vnf_instances": 

1707 engine_topic = "vnfrs" 

1708 elif main_topic == "vnflcm": 

1709 if topic == "vnf_lcm_op_occs": 

1710 engine_topic = "vnflcmops" 

1711 elif main_topic == "nst": 

1712 engine_topic = "nsts" 

1713 elif main_topic == "nsilcm": 

1714 engine_topic = "nsis" 

1715 if topic == "nsi_lcm_op_occs": 

1716 engine_topic = "nsilcmops" 

1717 elif main_topic == "pdu": 

1718 engine_topic = "pdus" 

1719 elif main_topic == "k8scluster": 

1720 engine_topic = "cluster" 

1721 if topic == "clusters" and _id == "register" or item == "deregister": 

1722 engine_topic = "clusterops" 

1723 elif topic == "infra_controller_profiles": 

1724 engine_topic = "infras_cont" 

1725 elif topic == "infra_config_profiles": 

1726 engine_topic = "infras_conf" 

1727 elif topic == "resource_profiles": 

1728 engine_topic = "resources" 

1729 elif topic == "app_profiles": 

1730 engine_topic = "apps" 

1731 elif main_topic == "k8scluster" and item in ( 

1732 "upgrade", 

1733 "get_creds", 

1734 "scale", 

1735 ): 

1736 engine_topic = "cluster" 

1737 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"): 

1738 engine_topic = "ksus" 

1739 if ( 

1740 engine_topic == "vims" 

1741 ): # TODO this is for backward compatibility, it will be removed in the future 

1742 engine_topic = "vim_accounts" 

1743 

1744 if topic == "subscriptions": 

1745 engine_topic = main_topic + "_" + topic 

1746 

1747 if method == "GET": 

1748 if item in ( 

1749 "nsd_content", 

1750 "package_content", 

1751 "artifacts", 

1752 "vnfd", 

1753 "nsd", 

1754 "nst", 

1755 "nst_content", 

1756 "ns_config_template", 

1757 ): 

1758 if item in ("vnfd", "nsd", "nst"): 

1759 path = "$DESCRIPTOR" 

1760 elif args: 

1761 path = args 

1762 elif item == "artifacts": 

1763 path = () 

1764 else: 

1765 path = None 

1766 file, _format = self.engine.get_file( 

1767 engine_session, 

1768 engine_topic, 

1769 _id, 

1770 path, 

1771 cherrypy.request.headers.get("Accept"), 

1772 ) 

1773 outdata = file 

1774 # elif not _id and topic != "clusters": 

1775 # outdata = self.engine.get_item_list( 

1776 # engine_session, engine_topic, kwargs, api_req=True 

1777 # ) 

1778 elif topic == "clusters" and item in ( 

1779 "infra_controller_profiles", 

1780 "infra_config_profiles", 

1781 "app_profiles", 

1782 "resource_profiles", 

1783 ): 

1784 profile = item 

1785 filter_q = None 

1786 outdata = self.engine.get_one_item( 

1787 engine_session, 

1788 engine_topic, 

1789 _id, 

1790 profile, 

1791 filter_q, 

1792 api_req=True, 

1793 ) 

1794 elif ( 

1795 topic == "clusters" 

1796 and item == "get_creds_file" 

1797 or item == "get_creds" 

1798 ): 

1799 if item == "get_creds_file": 

1800 op_id = args[0] 

1801 file, _format = self.engine.get_cluster_creds_file( 

1802 engine_session, engine_topic, _id, item, op_id 

1803 ) 

1804 outdata = file 

1805 if item == "get_creds": 

1806 op_id = self.engine.get_cluster_creds( 

1807 engine_session, engine_topic, _id, item 

1808 ) 

1809 outdata = {"op_id": op_id} 

1810 elif topic == "clusters" and not _id: 

1811 outdata = self.engine.get_item_list_cluster( 

1812 engine_session, engine_topic, kwargs, api_req=True 

1813 ) 

1814 elif not _id: 

1815 outdata = self.engine.get_item_list( 

1816 engine_session, engine_topic, kwargs, api_req=True 

1817 ) 

1818 else: 

1819 if item == "reports": 

1820 # TODO check that project_id (_id in this context) has permissions 

1821 _id = args[0] 

1822 filter_q = None 

1823 if "vcaStatusRefresh" in kwargs: 

1824 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]} 

1825 outdata = self.engine.get_item( 

1826 engine_session, engine_topic, _id, filter_q, True 

1827 ) 

1828 

1829 elif method == "POST": 

1830 cherrypy.response.status = HTTPStatus.CREATED.value 

1831 if topic in ( 

1832 "ns_descriptors_content", 

1833 "vnf_packages_content", 

1834 "netslice_templates_content", 

1835 "ns_config_template", 

1836 ): 

1837 _id = cherrypy.request.headers.get("Transaction-Id") 

1838 

1839 if not _id: 

1840 _id, _ = self.engine.new_item( 

1841 rollback, 

1842 engine_session, 

1843 engine_topic, 

1844 {}, 

1845 None, 

1846 cherrypy.request.headers, 

1847 ) 

1848 completed = self.engine.upload_content( 

1849 engine_session, 

1850 engine_topic, 

1851 _id, 

1852 indata, 

1853 kwargs, 

1854 cherrypy.request.headers, 

1855 ) 

1856 if completed: 

1857 self._set_location_header(main_topic, version, topic, _id) 

1858 else: 

1859 cherrypy.response.headers["Transaction-Id"] = _id 

1860 outdata = {"id": _id} 

1861 elif topic == "oka_packages": 

1862 _id = cherrypy.request.headers.get("Transaction-Id") 

1863 

1864 if not _id: 

1865 _id, _ = self.engine.new_item( 

1866 rollback, 

1867 engine_session, 

1868 engine_topic, 

1869 {}, 

1870 kwargs, 

1871 cherrypy.request.headers, 

1872 ) 

1873 cherrypy.request.headers["method"] = cherrypy.request.method 

1874 if indata: 

1875 completed = self.engine.upload_content( 

1876 engine_session, 

1877 engine_topic, 

1878 _id, 

1879 indata, 

1880 None, 

1881 cherrypy.request.headers, 

1882 ) 

1883 if completed: 

1884 self._set_location_header(main_topic, version, topic, _id) 

1885 else: 

1886 cherrypy.response.headers["Transaction-Id"] = _id 

1887 outdata = {"_id": _id, "id": _id} 

1888 elif topic == "ns_instances_content": 

1889 # creates NSR 

1890 _id, _ = self.engine.new_item( 

1891 rollback, engine_session, engine_topic, indata, kwargs 

1892 ) 

1893 # creates nslcmop 

1894 indata["lcmOperationType"] = "instantiate" 

1895 indata["nsInstanceId"] = _id 

1896 nslcmop_id, nsName, _ = self.engine.new_item( 

1897 rollback, engine_session, "nslcmops", indata, None 

1898 ) 

1899 self._set_location_header(main_topic, version, topic, _id) 

1900 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName} 

1901 elif topic == "ns_instances_terminate": 

1902 if indata.get("ns_ids"): 

1903 for ns_id in indata.get("ns_ids"): 

1904 nslcmop_desc = { 

1905 "lcmOperationType": "terminate", 

1906 "nsInstanceId": ns_id, 

1907 "autoremove": ( 

1908 indata.get("autoremove") 

1909 if "autoremove" in indata 

1910 else True 

1911 ), 

1912 } 

1913 op_id, _, _ = self.engine.new_item( 

1914 rollback, 

1915 engine_session, 

1916 "nslcmops", 

1917 nslcmop_desc, 

1918 kwargs, 

1919 ) 

1920 if not op_id: 

1921 _ = self.engine.del_item( 

1922 engine_session, engine_topic, ns_id 

1923 ) 

1924 outdata = {"ns_ids": indata.get("ns_ids")} 

1925 cherrypy.response.status = HTTPStatus.ACCEPTED.value 

1926 elif topic == "ns_instances" and item: 

1927 indata["lcmOperationType"] = item 

1928 indata["nsInstanceId"] = _id 

1929 _id, nsName, _ = self.engine.new_item( 

1930 rollback, engine_session, "nslcmops", indata, kwargs 

1931 ) 

1932 self._set_location_header( 

1933 main_topic, version, "ns_lcm_op_occs", _id 

1934 ) 

1935 outdata = {"id": _id, "nsName": nsName} 

1936 cherrypy.response.status = HTTPStatus.ACCEPTED.value 

1937 elif topic == "netslice_instances_content": 

1938 # creates NetSlice_Instance_record (NSIR) 

1939 _id, _ = self.engine.new_item( 

1940 rollback, engine_session, engine_topic, indata, kwargs 

1941 ) 

1942 self._set_location_header(main_topic, version, topic, _id) 

1943 indata["lcmOperationType"] = "instantiate" 

1944 indata["netsliceInstanceId"] = _id 

1945 nsilcmop_id, _ = self.engine.new_item( 

1946 rollback, engine_session, "nsilcmops", indata, kwargs 

1947 ) 

1948 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id} 

1949 elif topic == "netslice_instances" and item: 

1950 indata["lcmOperationType"] = item 

1951 indata["netsliceInstanceId"] = _id 

1952 _id, _ = self.engine.new_item( 

1953 rollback, engine_session, "nsilcmops", indata, kwargs 

1954 ) 

1955 self._set_location_header( 

1956 main_topic, version, "nsi_lcm_op_occs", _id 

1957 ) 

1958 outdata = {"id": _id} 

1959 cherrypy.response.status = HTTPStatus.ACCEPTED.value 

1960 elif topic == "vnf_packages" and item == "action": 

1961 indata["lcmOperationType"] = item 

1962 indata["vnfPkgId"] = _id 

1963 _id, _ = self.engine.new_item( 

1964 rollback, engine_session, "vnfpkgops", indata, kwargs 

1965 ) 

1966 self._set_location_header( 

1967 main_topic, version, "vnfpkg_op_occs", _id 

1968 ) 

1969 outdata = {"id": _id} 

1970 cherrypy.response.status = HTTPStatus.ACCEPTED.value 

1971 elif topic == "subscriptions": 

1972 _id, _ = self.engine.new_item( 

1973 rollback, engine_session, engine_topic, indata, kwargs 

1974 ) 

1975 self._set_location_header(main_topic, version, topic, _id) 

1976 link = {} 

1977 link["self"] = cherrypy.response.headers["Location"] 

1978 outdata = { 

1979 "id": _id, 

1980 "filter": indata["filter"], 

1981 "callbackUri": indata["CallbackUri"], 

1982 "_links": link, 

1983 } 

1984 cherrypy.response.status = HTTPStatus.CREATED.value 

1985 elif topic == "vnf_instances" and item: 

1986 indata["lcmOperationType"] = item 

1987 indata["vnfInstanceId"] = _id 

1988 _id, nsName, _ = self.engine.new_item( 

1989 rollback, engine_session, "vnflcmops", indata, kwargs 

1990 ) 

1991 self._set_location_header( 

1992 main_topic, version, "vnf_lcm_op_occs", _id 

1993 ) 

1994 outdata = {"id": _id, "nsName": nsName} 

1995 cherrypy.response.status = HTTPStatus.ACCEPTED.value 

1996 elif topic == "ns_lcm_op_occs" and item == "cancel": 

1997 indata["nsLcmOpOccId"] = _id 

1998 self.engine.cancel_item( 

1999 rollback, engine_session, "nslcmops", indata, None 

2000 ) 

2001 self._set_location_header(main_topic, version, topic, _id) 

2002 cherrypy.response.status = HTTPStatus.ACCEPTED.value 

2003 elif topic == "clusters" and _id == "register": 

2004 # To register a cluster 

2005 _id, _ = self.engine.add_item( 

2006 rollback, engine_session, engine_topic, indata, kwargs 

2007 ) 

2008 self._set_location_header(main_topic, version, topic, _id) 

2009 outdata = {"_id": _id, "id": _id} 

2010 elif ( 

2011 topic 

2012 in ( 

2013 "clusters", 

2014 "infra_controller_profiles", 

2015 "infra_config_profiles", 

2016 "app_profiles", 

2017 "resource_profiles", 

2018 ) 

2019 and item is None 

2020 ): 

2021 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles 

2022 _id, _ = self.engine.new_item( 

2023 rollback, engine_session, engine_topic, indata, kwargs 

2024 ) 

2025 self._set_location_header(main_topic, version, topic, _id) 

2026 outdata = {"_id": _id, "id": _id} 

2027 elif topic == "ksus" and item: 

2028 if item == "clone": 

2029 _id = self.engine.clone( 

2030 rollback, 

2031 engine_session, 

2032 engine_topic, 

2033 _id, 

2034 indata, 

2035 kwargs, 

2036 cherrypy.request.headers, 

2037 ) 

2038 self._set_location_header(main_topic, version, topic, _id) 

2039 outdata = {"_id": _id, "id": _id} 

2040 if item == "move": 

2041 op_id = self.engine.move_ksu( 

2042 engine_session, engine_topic, _id, indata, kwargs 

2043 ) 

2044 outdata = {"op_id": op_id} 

2045 elif topic == "ksus" and _id == "delete": 

2046 op_id = self.engine.delete_ksu( 

2047 engine_session, engine_topic, _id, indata 

2048 ) 

2049 outdata = {"op_id": op_id} 

2050 elif topic == "ksus" and _id == "update": 

2051 op_id = self.engine.edit_item( 

2052 engine_session, engine_topic, _id, indata, kwargs 

2053 ) 

2054 outdata = {"op_id": op_id} 

2055 elif topic == "clusters" and item in ("upgrade", "scale"): 

2056 op_id = self.engine.update_cluster( 

2057 engine_session, engine_topic, _id, item, indata 

2058 ) 

2059 outdata = {"op_id": op_id} 

2060 else: 

2061 _id, op_id = self.engine.new_item( 

2062 rollback, 

2063 engine_session, 

2064 engine_topic, 

2065 indata, 

2066 kwargs, 

2067 cherrypy.request.headers, 

2068 ) 

2069 self._set_location_header(main_topic, version, topic, _id) 

2070 outdata = {"_id": _id, "id": _id} 

2071 if op_id: 

2072 outdata["op_id"] = op_id 

2073 cherrypy.response.status = HTTPStatus.ACCEPTED.value 

2074 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages") 

2075 

2076 elif method == "DELETE": 

2077 if not _id: 

2078 outdata = self.engine.del_item_list( 

2079 engine_session, engine_topic, kwargs 

2080 ) 

2081 cherrypy.response.status = HTTPStatus.OK.value 

2082 else: # len(args) > 1 

2083 # for NS NSI generate an operation 

2084 op_id = None 

2085 if topic == "ns_instances_content" and not engine_session["force"]: 

2086 nslcmop_desc = { 

2087 "lcmOperationType": "terminate", 

2088 "nsInstanceId": _id, 

2089 "autoremove": True, 

2090 } 

2091 op_id, nsName, _ = self.engine.new_item( 

2092 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs 

2093 ) 

2094 if op_id: 

2095 outdata = {"_id": op_id, "nsName": nsName} 

2096 elif ( 

2097 topic == "netslice_instances_content" 

2098 and not engine_session["force"] 

2099 ): 

2100 nsilcmop_desc = { 

2101 "lcmOperationType": "terminate", 

2102 "netsliceInstanceId": _id, 

2103 "autoremove": True, 

2104 } 

2105 op_id, _ = self.engine.new_item( 

2106 rollback, engine_session, "nsilcmops", nsilcmop_desc, None 

2107 ) 

2108 if op_id: 

2109 outdata = {"_id": op_id} 

2110 elif topic == "clusters" and item == "deregister": 

2111 if not op_id: 

2112 op_id = self.engine.remove( 

2113 engine_session, engine_topic, _id 

2114 ) 

2115 if op_id: 

2116 outdata = {"_id": op_id} 

2117 cherrypy.response.status = ( 

2118 HTTPStatus.ACCEPTED.value 

2119 if op_id 

2120 else HTTPStatus.NO_CONTENT.value 

2121 ) 

2122 elif topic == "ksus": 

2123 op_id = self.engine.delete_ksu( 

2124 engine_session, engine_topic, _id, indata 

2125 ) 

2126 outdata = {"op_id": op_id} 

2127 # if there is not any deletion in process, delete 

2128 elif not op_id: 

2129 op_id = self.engine.del_item(engine_session, engine_topic, _id) 

2130 if op_id: 

2131 outdata = {"_id": op_id} 

2132 cherrypy.response.status = ( 

2133 HTTPStatus.ACCEPTED.value 

2134 if op_id 

2135 else HTTPStatus.NO_CONTENT.value 

2136 ) 

2137 

2138 elif method in ("PUT", "PATCH"): 

2139 op_id = None 

2140 if not indata and not kwargs and not engine_session.get("set_project"): 

2141 raise NbiException( 

2142 "Nothing to update. Provide payload and/or query string", 

2143 HTTPStatus.BAD_REQUEST, 

2144 ) 

2145 if ( 

2146 item 

2147 in ( 

2148 "nsd_content", 

2149 "package_content", 

2150 "nst_content", 

2151 "template_content", 

2152 ) 

2153 and method == "PUT" 

2154 ): 

2155 completed = self.engine.upload_content( 

2156 engine_session, 

2157 engine_topic, 

2158 _id, 

2159 indata, 

2160 kwargs, 

2161 cherrypy.request.headers, 

2162 ) 

2163 if not completed: 

2164 cherrypy.response.headers["Transaction-Id"] = id 

2165 elif item in ( 

2166 "app_profiles", 

2167 "resource_profiles", 

2168 "infra_controller_profiles", 

2169 "infra_config_profiles", 

2170 ): 

2171 op_id = self.engine.edit( 

2172 engine_session, engine_topic, _id, item, indata, kwargs 

2173 ) 

2174 elif topic == "oka_packages" and method == "PATCH": 

2175 if kwargs: 

2176 op_id = self.engine.edit_item( 

2177 engine_session, engine_topic, _id, None, kwargs 

2178 ) 

2179 if indata: 

2180 if isinstance(indata, dict): 

2181 op_id = self.engine.edit_item( 

2182 engine_session, engine_topic, _id, indata, kwargs 

2183 ) 

2184 else: 

2185 cherrypy.request.headers["method"] = cherrypy.request.method 

2186 completed = self.engine.upload_content( 

2187 engine_session, 

2188 engine_topic, 

2189 _id, 

2190 indata, 

2191 {}, 

2192 cherrypy.request.headers, 

2193 ) 

2194 if not completed: 

2195 cherrypy.response.headers["Transaction-Id"] = id 

2196 elif topic == "oka_packages" and method == "PUT": 

2197 if indata: 

2198 cherrypy.request.headers["method"] = cherrypy.request.method 

2199 completed = self.engine.upload_content( 

2200 engine_session, 

2201 engine_topic, 

2202 _id, 

2203 indata, 

2204 {}, 

2205 cherrypy.request.headers, 

2206 ) 

2207 if not completed: 

2208 cherrypy.response.headers["Transaction-Id"] = id 

2209 else: 

2210 op_id = self.engine.edit_item( 

2211 engine_session, engine_topic, _id, indata, kwargs 

2212 ) 

2213 

2214 if op_id: 

2215 cherrypy.response.status = HTTPStatus.ACCEPTED.value 

2216 outdata = {"op_id": op_id} 

2217 else: 

2218 cherrypy.response.status = HTTPStatus.NO_CONTENT.value 

2219 outdata = None 

2220 else: 

2221 raise NbiException( 

2222 "Method {} not allowed".format(method), 

2223 HTTPStatus.METHOD_NOT_ALLOWED, 

2224 ) 

2225 

2226 # if Role information changes, it is needed to reload the information of roles 

2227 if topic == "roles" and method != "GET": 

2228 self.authenticator.load_operation_to_allowed_roles() 

2229 

2230 if ( 

2231 topic == "projects" 

2232 and method == "DELETE" 

2233 or topic in ["users", "roles"] 

2234 and method in ["PUT", "PATCH", "DELETE"] 

2235 ): 

2236 self.authenticator.remove_token_from_cache() 

2237 

2238 cef_event( 

2239 cef_logger, 

2240 { 

2241 "name": "User Operation", 

2242 "sourceUserName": token_info.get("username"), 

2243 }, 

2244 ) 

2245 if topic == "ns_instances_content" and url_id: 

2246 nsName = ( 

2247 outdata.get("name") if method == "GET" else outdata.get("nsName") 

2248 ) 

2249 cef_event( 

2250 cef_logger, 

2251 { 

2252 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format( 

2253 log_mapping[method], 

2254 topic, 

2255 nsName, 

2256 outdata.get("id"), 

2257 token_info.get("project_name"), 

2258 ), 

2259 }, 

2260 ) 

2261 cherrypy.log("{}".format(cef_logger)) 

2262 elif topic == "ns_instances_content" and method == "POST": 

2263 cef_event( 

2264 cef_logger, 

2265 { 

2266 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format( 

2267 log_mapping[method], 

2268 topic, 

2269 outdata.get("nsName"), 

2270 outdata.get("id"), 

2271 token_info.get("project_name"), 

2272 ), 

2273 }, 

2274 ) 

2275 cherrypy.log("{}".format(cef_logger)) 

2276 elif topic in ("ns_instances", "vnf_instances") and item: 

2277 cef_event( 

2278 cef_logger, 

2279 { 

2280 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format( 

2281 log_mapping[method], 

2282 topic, 

2283 outdata.get("nsName"), 

2284 url_id, 

2285 token_info.get("project_name"), 

2286 ), 

2287 }, 

2288 ) 

2289 cherrypy.log("{}".format(cef_logger)) 

2290 elif item is not None: 

2291 cef_event( 

2292 cef_logger, 

2293 { 

2294 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format( 

2295 item, 

2296 topic, 

2297 url_id, 

2298 token_info.get("project_name"), 

2299 ), 

2300 }, 

2301 ) 

2302 cherrypy.log("{}".format(cef_logger)) 

2303 else: 

2304 cef_event( 

2305 cef_logger, 

2306 { 

2307 "message": "{} {} {}, Project={} Outcome=Success".format( 

2308 log_mapping[method], 

2309 topic, 

2310 url_id, 

2311 token_info.get("project_name"), 

2312 ), 

2313 }, 

2314 ) 

2315 cherrypy.log("{}".format(cef_logger)) 

2316 return self._format_out(outdata, token_info, _format) 

2317 except Exception as e: 

2318 if isinstance( 

2319 e, 

2320 ( 

2321 NbiException, 

2322 EngineException, 

2323 DbException, 

2324 FsException, 

2325 MsgException, 

2326 AuthException, 

2327 ValidationError, 

2328 AuthconnException, 

2329 ), 

2330 ): 

2331 http_code_value = cherrypy.response.status = e.http_code.value 

2332 http_code_name = e.http_code.name 

2333 cherrypy.log("Exception {}".format(e)) 

2334 else: 

2335 http_code_value = ( 

2336 cherrypy.response.status 

2337 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR 

2338 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True) 

2339 http_code_name = HTTPStatus.BAD_REQUEST.name 

2340 if hasattr(outdata, "close"): # is an open file 

2341 outdata.close() 

2342 error_text = str(e) 

2343 rollback.reverse() 

2344 for rollback_item in rollback: 

2345 try: 

2346 if rollback_item.get("operation") == "set": 

2347 self.engine.db.set_one( 

2348 rollback_item["topic"], 

2349 {"_id": rollback_item["_id"]}, 

2350 rollback_item["content"], 

2351 fail_on_empty=False, 

2352 ) 

2353 elif rollback_item.get("operation") == "del_list": 

2354 self.engine.db.del_list( 

2355 rollback_item["topic"], 

2356 rollback_item["filter"], 

2357 ) 

2358 else: 

2359 self.engine.db.del_one( 

2360 rollback_item["topic"], 

2361 {"_id": rollback_item["_id"]}, 

2362 fail_on_empty=False, 

2363 ) 

2364 except Exception as e2: 

2365 rollback_error_text = "Rollback Exception {}: {}".format( 

2366 rollback_item, e2 

2367 ) 

2368 cherrypy.log(rollback_error_text) 

2369 error_text += ". " + rollback_error_text 

2370 # if isinstance(e, MsgException): 

2371 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format( 

2372 # engine_topic[:-1], method, error_text) 

2373 problem_details = { 

2374 "code": http_code_name, 

2375 "status": http_code_value, 

2376 "detail": error_text, 

2377 } 

2378 if item is not None and token_info is not None: 

2379 cef_event( 

2380 cef_logger, 

2381 { 

2382 "name": "User Operation", 

2383 "sourceUserName": token_info.get("username", None), 

2384 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format( 

2385 item, 

2386 topic, 

2387 url_id, 

2388 token_info.get("project_name", None), 

2389 ), 

2390 "severity": "2", 

2391 }, 

2392 ) 

2393 cherrypy.log("{}".format(cef_logger)) 

2394 elif token_info is not None: 

2395 cef_event( 

2396 cef_logger, 

2397 { 

2398 "name": "User Operation", 

2399 "sourceUserName": token_info.get("username", None), 

2400 "message": "{} {} {}, Project={} Outcome=Failure".format( 

2401 item, 

2402 topic, 

2403 url_id, 

2404 token_info.get("project_name", None), 

2405 ), 

2406 "severity": "2", 

2407 }, 

2408 ) 

2409 cherrypy.log("{}".format(cef_logger)) 

2410 return self._format_out(problem_details, token_info) 

2411 # raise cherrypy.HTTPError(e.http_code.value, str(e)) 

2412 finally: 

2413 if token_info: 

2414 self._format_login(token_info) 

2415 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict): 

2416 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"): 

2417 if outdata.get(logging_id): 

2418 cherrypy.request.login += ";{}={}".format( 

2419 logging_id, outdata[logging_id][:36] 

2420 ) 

2421 

2422 

2423def _start_service(): 

2424 """ 

2425 Callback function called when cherrypy.engine starts 

2426 Override configuration with env variables 

2427 Set database, storage, message configuration 

2428 Init database with admin/admin user password 

2429 """ 

2430 global subscription_thread 

2431 global cef_logger 

2432 global current_backend 

2433 cherrypy.log.error("Starting osm_nbi") 

2434 # update general cherrypy configuration 

2435 update_dict = {} 

2436 

2437 engine_config = cherrypy.tree.apps["/osm"].config 

2438 for k, v in environ.items(): 

2439 if k == "OSMNBI_USER_MANAGEMENT": 

2440 feature_state = v.lower() == "true" 

2441 engine_config["authentication"]["user_management"] = feature_state 

2442 elif k == "OSMNBI_PWD_EXPIRE_DAYS": 

2443 pwd_expire_days = int(v) 

2444 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days 

2445 elif k == "OSMNBI_MAX_PWD_ATTEMPT": 

2446 max_pwd_attempt = int(v) 

2447 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt 

2448 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS": 

2449 account_expire_days = int(v) 

2450 engine_config["authentication"]["account_expire_days"] = account_expire_days 

2451 elif k == "OSMNBI_SMTP_SERVER": 

2452 engine_config["authentication"]["smtp_server"] = v 

2453 engine_config["authentication"]["all"] = environ 

2454 elif k == "OSMNBI_SMTP_PORT": 

2455 port = int(v) 

2456 engine_config["authentication"]["smtp_port"] = port 

2457 elif k == "OSMNBI_SENDER_EMAIL": 

2458 engine_config["authentication"]["sender_email"] = v 

2459 elif k == "OSMNBI_EMAIL_PASSWORD": 

2460 engine_config["authentication"]["sender_password"] = v 

2461 elif k == "OSMNBI_OTP_RETRY_COUNT": 

2462 otp_retry_count = int(v) 

2463 engine_config["authentication"]["retry_count"] = otp_retry_count 

2464 elif k == "OSMNBI_OTP_EXPIRY_TIME": 

2465 otp_expiry_time = int(v) 

2466 engine_config["authentication"]["otp_expiry_time"] = otp_expiry_time 

2467 if not k.startswith("OSMNBI_"): 

2468 continue 

2469 k1, _, k2 = k[7:].lower().partition("_") 

2470 if not k2: 

2471 continue 

2472 try: 

2473 # update static configuration 

2474 if k == "OSMNBI_STATIC_DIR": 

2475 engine_config["/static"]["tools.staticdir.dir"] = v 

2476 engine_config["/static"]["tools.staticdir.on"] = True 

2477 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT": 

2478 update_dict["server.socket_port"] = int(v) 

2479 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST": 

2480 update_dict["server.socket_host"] = v 

2481 elif k1 in ("server", "test", "auth", "log"): 

2482 update_dict[k1 + "." + k2] = v 

2483 elif k1 in ("message", "database", "storage", "authentication"): 

2484 # k2 = k2.replace('_', '.') 

2485 if k2 in ("port", "db_port"): 

2486 engine_config[k1][k2] = int(v) 

2487 else: 

2488 engine_config[k1][k2] = v 

2489 

2490 except ValueError as e: 

2491 cherrypy.log.error("Ignoring environ '{}': " + str(e)) 

2492 except Exception as e: 

2493 cherrypy.log( 

2494 "WARNING: skipping environ '{}' on exception '{}'".format(k, e) 

2495 ) 

2496 

2497 if update_dict: 

2498 cherrypy.config.update(update_dict) 

2499 engine_config["global"].update(update_dict) 

2500 

2501 # logging cherrypy 

2502 log_format_simple = ( 

2503 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s" 

2504 ) 

2505 log_formatter_simple = logging.Formatter( 

2506 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S" 

2507 ) 

2508 logger_server = logging.getLogger("cherrypy.error") 

2509 logger_access = logging.getLogger("cherrypy.access") 

2510 logger_cherry = logging.getLogger("cherrypy") 

2511 logger_nbi = logging.getLogger("nbi") 

2512 

2513 if "log.file" in engine_config["global"]: 

2514 file_handler = logging.handlers.RotatingFileHandler( 

2515 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0 

2516 ) 

2517 file_handler.setFormatter(log_formatter_simple) 

2518 logger_cherry.addHandler(file_handler) 

2519 logger_nbi.addHandler(file_handler) 

2520 # log always to standard output 

2521 for format_, logger in { 

2522 "nbi.server %(filename)s:%(lineno)s": logger_server, 

2523 "nbi.access %(filename)s:%(lineno)s": logger_access, 

2524 "%(name)s %(filename)s:%(lineno)s": logger_nbi, 

2525 }.items(): 

2526 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_) 

2527 log_formatter_cherry = logging.Formatter( 

2528 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S" 

2529 ) 

2530 str_handler = logging.StreamHandler() 

2531 str_handler.setFormatter(log_formatter_cherry) 

2532 logger.addHandler(str_handler) 

2533 

2534 if engine_config["global"].get("log.level"): 

2535 logger_cherry.setLevel(engine_config["global"]["log.level"]) 

2536 logger_nbi.setLevel(engine_config["global"]["log.level"]) 

2537 

2538 # logging other modules 

2539 for k1, logname in { 

2540 "message": "nbi.msg", 

2541 "database": "nbi.db", 

2542 "storage": "nbi.fs", 

2543 }.items(): 

2544 engine_config[k1]["logger_name"] = logname 

2545 logger_module = logging.getLogger(logname) 

2546 if "logfile" in engine_config[k1]: 

2547 file_handler = logging.handlers.RotatingFileHandler( 

2548 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0 

2549 ) 

2550 file_handler.setFormatter(log_formatter_simple) 

2551 logger_module.addHandler(file_handler) 

2552 if "loglevel" in engine_config[k1]: 

2553 logger_module.setLevel(engine_config[k1]["loglevel"]) 

2554 # TODO add more entries, e.g.: storage 

2555 cherrypy.tree.apps["/osm"].root.engine.start(engine_config) 

2556 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config) 

2557 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version) 

2558 cherrypy.tree.apps["/osm"].root.authenticator.init_db( 

2559 target_version=auth_database_version 

2560 ) 

2561 

2562 cef_logger = cef_event_builder(engine_config["authentication"]) 

2563 

2564 # start subscriptions thread: 

2565 subscription_thread = SubscriptionThread( 

2566 config=engine_config, engine=nbi_server.engine 

2567 ) 

2568 subscription_thread.start() 

2569 # Do not capture except SubscriptionException 

2570 

2571 backend = engine_config["authentication"]["backend"] 

2572 current_backend = backend 

2573 cherrypy.log.error( 

2574 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format( 

2575 nbi_version, nbi_version_date, backend 

2576 ) 

2577 ) 

2578 

2579 

2580def _stop_service(): 

2581 """ 

2582 Callback function called when cherrypy.engine stops 

2583 TODO: Ending database connections. 

2584 """ 

2585 global subscription_thread 

2586 if subscription_thread: 

2587 subscription_thread.terminate() 

2588 subscription_thread = None 

2589 cherrypy.tree.apps["/osm"].root.engine.stop() 

2590 cherrypy.log.error("Stopping osm_nbi") 

2591 

2592 

2593def nbi(config_file): 

2594 global nbi_server 

2595 nbi_server = Server() 

2596 cherrypy.engine.subscribe("start", _start_service) 

2597 cherrypy.engine.subscribe("stop", _stop_service) 

2598 cherrypy.quickstart(nbi_server, "/osm", config_file) 

2599 

2600 

2601def usage(): 

2602 print( 

2603 """Usage: {} [options] 

2604 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg) 

2605 -h|--help: shows this help 

2606 """.format( 

2607 sys.argv[0] 

2608 ) 

2609 ) 

2610 # --log-socket-host HOST: send logs to this host") 

2611 # --log-socket-port PORT: send logs using this port (default: 9022)") 

2612 

2613 

2614if __name__ == "__main__": 

2615 try: 

2616 # load parameters and configuration 

2617 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"]) 

2618 # TODO add "log-socket-host=", "log-socket-port=", "log-file=" 

2619 config_file = None 

2620 for o, a in opts: 

2621 if o in ("-h", "--help"): 

2622 usage() 

2623 sys.exit() 

2624 elif o in ("-c", "--config"): 

2625 config_file = a 

2626 # elif o == "--log-socket-port": 

2627 # log_socket_port = a 

2628 # elif o == "--log-socket-host": 

2629 # log_socket_host = a 

2630 # elif o == "--log-file": 

2631 # log_file = a 

2632 else: 

2633 assert False, "Unhandled option" 

2634 if config_file: 

2635 if not path.isfile(config_file): 

2636 print( 

2637 "configuration file '{}' that not exist".format(config_file), 

2638 file=sys.stderr, 

2639 ) 

2640 exit(1) 

2641 else: 

2642 for config_file in ( 

2643 __file__[: __file__.rfind(".")] + ".cfg", 

2644 "./nbi.cfg", 

2645 "/etc/osm/nbi.cfg", 

2646 ): 

2647 if path.isfile(config_file): 

2648 break 

2649 else: 

2650 print( 

2651 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/", 

2652 file=sys.stderr, 

2653 ) 

2654 exit(1) 

2655 nbi(config_file) 

2656 except getopt.GetoptError as e: 

2657 print(str(e), file=sys.stderr) 

2658 # usage() 

2659 exit(1)