Add juju support to lcm
[osm/RO.git] / lcm / lcm.py
1 #!/usr/bin/python3
2 # -*- coding: utf-8 -*-
3
4
5 import argparse
6 import glob
7 from juju_api import JujuApi
8 import mimetypes
9 import os
10 import os.path
11 import re
12 import shutil
13 import tarfile
14
15 import asyncio
16 import aiohttp
17 import yaml
18 import ROclient
19 import time
20 import dbmemory
21 import logging
22
23 from copy import deepcopy
24 from uuid import uuid4
25
26 #streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
27 streamformat = "%(name)s %(levelname)s: %(message)s"
28 logging.basicConfig(format=streamformat, level=logging.DEBUG)
29 logger = logging.getLogger('lcm')
30
31 ro_account = {
32 "url": "http://10.105.129.121:9090/openmano",
33 "tenant": "osm",
34 }
35
36 vca_account = {
37 "ip": "10.105.129.40",
38 "port": 17070,
39 "user": "admin",
40 "secret": "NzdjM2M4ODA5NjlhNzRkZGJhMzc2NjNk",
41 }
42
43 # These functions are written to use the JujuApi class from juju_api.py, a
44 # drop-in copy of the one used in OSM today. This will make it easier to extend
45 # functionality in the LCM as it's added to the Juju API
46
47
48 def GetJujuApi(loop):
49 # Quiet logging from the websocket library. If you want to see
50 # everything sent over the wire, set this to DEBUG.
51 logging.basicConfig(level=logging.DEBUG)
52
53 ws_logger = logging.getLogger('websockets.protocol')
54 ws_logger.setLevel(logging.INFO)
55
56 api = JujuApi(server=vca_account['ip'],
57 port=vca_account['port'],
58 user=vca_account['user'],
59 secret=vca_account['secret'],
60 loop=loop,
61 log=ws_logger,
62 model_name='default'
63 )
64 return api
65
66
67 def get_vnf_unique_name(nsr_name, vnfr_name, member_vnf_index):
68 """Get the unique VNF name.
69 Charm names accepts only a to z and non-consecutive - characters."""
70 name = "{}-{}-{}".format(nsr_name, vnfr_name, member_vnf_index)
71 new_name = ''
72 for c in name:
73 if c.isdigit():
74 c = chr(97 + int(c))
75 elif not c.isalpha():
76 c = "-"
77 new_name += c
78 return re.sub('\-+', '-', new_name.lower())
79
80
81 async def DeployApplication(loop, application_name, charm_path, config):
82 """
83 Deploy a charm.
84
85 Deploy a VNF configuration charm from a local directory.
86 :param object loop: The event loop
87 :param str application_name: The unique name of this application.
88 :param str charm_path: The path to the charm.
89
90 :Example:
91
92 DeployApplication(loop, ".cache/ping_vnf/charm/pingpong", "ping_vnf")
93 """
94
95 api = GetJujuApi(loop)
96
97 await api.login()
98 if api.authenticated:
99 charm = os.path.basename(charm_path)
100
101 await api.deploy_application(charm,
102 name=application_name,
103 path=charm_path,
104 )
105 await api.apply_config(config, application_name)
106
107 # Wait for the application to fully deploy. This will block until the
108 # agent is in an idle state, and the charm's workload is either
109 # 'active' or 'unknown', meaning it's ready but the author did not
110 # explicitly set a workload state.
111 # print("Waiting for application '{}' to deploy...".format(charm))
112 while (True):
113 # Deploy the charm and wait, periodically checking its status
114 await api.wait_for_application(charm, 30)
115
116 error = await api.is_application_error(charm)
117 if error:
118 print("This application is in an error state.")
119 break
120
121 blocked = await api.is_application_blocked(charm)
122 if blocked:
123 print("This application is blocked.")
124 break
125
126 # An extra check to see if the charm is ready
127 up = await api.is_application_up(charm)
128 # print("Application is {}".format("up" if up else "down"))
129
130 print("Application {} is deployed".format(args.application))
131 await api.logout()
132
133
134 async def RemoveApplication(loop, application_name):
135 """
136 Remove an application from the Juju Controller
137
138 Removed the named application and it's charm from the Juju controller.
139
140 :param object loop: The event loop.
141 :param str application_name: The unique name of the application.
142
143 :Example:
144
145 RemoveApplication(loop, "ping_vnf")
146 RemoveApplication(loop, "pong_vnf")
147 """
148 api = GetJujuApi(loop)
149
150 await api.login()
151 if api.authenticated:
152 print("Removing application {}".format(application_name))
153 await api.remove_application(application_name)
154 await api.logout()
155
156 # conains created tasks/futures to be able to cancel
157 lcm_tasks = {}
158
159 headers_req = {'Accept': 'application/yaml', 'content-type': 'application/yaml'}
160 ns_status = ("CREATION-SCHEDULED", "DEPLOYING", "CONFIGURING", "DELETION-SCHEDULED", "UN-CONFIGURING", "UNDEPLOYING")
161
162 # TODO replace with database calls
163 db = dbmemory.dbmemory()
164
165
166 class RO_Exception(Exception):
167 pass
168
169
170 async def CreateNS(loop, nsr_id):
171 logger.debug("CreateNS task nsr_id={} Enter".format(nsr_id))
172 nsr_lcm = {
173 "id": nsr_id,
174 "RO": {"vnfd_id": {}, "nsd_id": None, "nsr_id": None, "nsr_status": "SCHEDULED"},
175 "nsr_ip": {},
176 "VCA": {"TODO"},
177 "status": "BUILD",
178 "status_detailed": "",
179 }
180
181 deloyment_timeout = 120
182 try:
183 ns_request = db.get_one("ns_request", {"id": nsr_id})
184 nsd = db.get_one("nsd", {"id": ns_request["nsd_id"]})
185 RO = ROclient.ROClient(loop, endpoint_url=ro_account["url"], tenant=ro_account["tenant"],
186 datacenter=ns_request["vim"])
187 nsr_lcm["status_detailed"] = "Creating vnfd at RO"
188 # ns_request["constituent-vnfr-ref"] = []
189
190 db.create("nsr_lcm", nsr_lcm)
191
192 # get vnfds, instantiate at RO
193 logger.debug("CreateNS task nsr_id={} RO VNFD".format(nsr_id))
194 for c_vnf in nsd["constituent-vnfd"]:
195 vnfd_id = c_vnf["vnfd-id-ref"]
196 vnfd = db.get_one("vnfd", {"id": vnfd_id})
197 vnfd.pop("_admin", None)
198 vnfd.pop("_id", None)
199 # vnfr = deepcopy(vnfd)
200 # vnfr["member-vnf-index"] = c_vnf["member-vnf-index"]
201 # vnfr["nsr-id"] = nsr_id
202 # vnfr["id"] = uuid4()
203 # vnfr["vnf-id"] = vnfd["id"]
204 # ns_request["constituent-vnfr-ref"],append(vnfd_id)
205
206 # TODO change id for RO in case it is present
207 try:
208 desc = await RO.create("vnfd", descriptor=vnfd)
209 nsr_lcm["RO"]["vnfd_id"][vnfd_id] = desc["uuid"]
210 db.replace("nsr_lcm", {"id": nsr_id}, nsr_lcm)
211 except ROclient.ROClientException as e:
212 if e.http_code == 409: # conflict, vnfd already present
213 print("debug", e)
214 else:
215 raise
216
217 # db_new("vnfr", vnfr)
218 # db_update("ns_request", nsr_id, ns_request)
219
220 # create nsd at RO
221 logger.debug("CreateNS task nsr_id={} RO NSD".format(nsr_id))
222 nsr_lcm["status_detailed"] = "Creating nsd at RO"
223 nsd_id = ns_request["nsd_id"]
224 nsd = db.get_one("nsd", {"id": nsd_id})
225 nsd.pop("_admin", None)
226 nsd.pop("_id", None)
227 try:
228 desc = await RO.create("nsd", descriptor=nsd)
229 nsr_lcm["RO"]["nsd_id"] = desc["uuid"]
230 db.replace("nsr_lcm", {"id": nsr_id}, nsr_lcm)
231 except ROclient.ROClientException as e:
232 if e.http_code == 409: # conflict, nsd already present
233 print("debug", e)
234 else:
235 raise
236
237 # Crate ns at RO
238 logger.debug("CreateNS task nsr_id={} RO NS".format(nsr_id))
239 nsr_lcm["status_detailed"] = "Creating ns at RO"
240 desc = await RO.create("ns", name=ns_request["name"], datacenter=ns_request["vim"], scenario=nsr_lcm["RO"]["nsd_id"])
241 RO_nsr_id = desc["uuid"]
242 nsr_lcm["RO"]["nsr_id"] = RO_nsr_id
243 nsr_lcm["RO"]["nsr_status"] = "BUILD"
244 db.replace("nsr_lcm", {"id": nsr_id}, nsr_lcm)
245
246 # wait until NS is ready
247 deloyment_timeout = 600
248 while deloyment_timeout > 0:
249 ns_status_detailed = "Waiting ns ready at RO"
250 nsr_lcm["status_detailed"] = ns_status_detailed
251 desc = await RO.show("ns", RO_nsr_id)
252 ns_status, ns_status_info = RO.check_ns_status(desc)
253 nsr_lcm["RO"]["nsr_status"] = ns_status
254 if ns_status == "ERROR":
255 raise ROclient.ROClientException(ns_status_info)
256 elif ns_status == "BUILD":
257 nsr_lcm["status_detailed"] = ns_status_detailed + "; nsr_id: '{}', {}".format(nsr_id, ns_status_info)
258 elif ns_status == "ACTIVE":
259 nsr_lcm["nsr_ip"] = RO.get_ns_vnf_ip(desc)
260 break
261 else:
262 assert False, "ROclient.check_ns_status returns unknown {}".format(ns_status)
263
264 await asyncio.sleep(5, loop=loop)
265 deloyment_timeout -= 5
266 if deloyment_timeout <= 0:
267 raise ROclient.ROClientException("Timeot wating ns to be ready")
268 nsr_lcm["status_detailed"] = "Configuring vnfr"
269 db.replace("nsr_lcm", {"id": nsr_id}, nsr_lcm)
270
271 #for nsd in nsr_lcm["descriptors"]["nsd"]:
272
273 logger.debug("CreateNS task nsr_id={} VCA look for".format(nsr_id))
274 for c_vnf in nsd["constituent-vnfd"]:
275 vnfd_id = c_vnf["vnfd-id-ref"]
276 vnfd_index = int(c_vnf["member-vnf-index"])
277 vnfd = db.get_one("vnfd", {"id": vnfd_id})
278 if vnfd.get("vnf-configuration") and vnfd["vnf-configuration"].get("juju"):
279 proxy_charm = vnfd["vnf-configuration"]["juju"]["charm"]
280 # config_primitive = vnfd["vnf-configuration"].get("config-primitive")
281 initial_config_primitive = vnfd["vnf-configuration"].get("initial-config-primitive")
282 # get parameters for juju charm
283 base_folder = vnfd["_admin"]["storage"]
284 path = base_folder + "/charms/" + proxy_charm
285 mgmt_ip = nsr_lcm['nsr_ip'][vnfd_index]
286
287 # TODO launch VCA charm
288 # task = asyncio.ensure_future(DeployCharm(loop, path, mgmt_ip, config_primitive))
289 config = {}
290 for primitive in initial_config_primitive:
291 if primitive['name'] == 'config':
292 for parameter in primitive['parameter']:
293 param = parameter['name']
294 if parameter['value'] == "<rw_mgmt_ip>":
295 config[param] = mgmt_ip
296 else:
297 config[param] = parameter['value']
298
299 task = asyncio.ensure_future(
300 DeployApplication(
301 loop,
302 get_vnf_unique_name(nsd_id, vnfd_id, vnfd_index),
303 path,
304 config,
305 )
306 )
307
308
309 nsr_lcm["status"] = "DONE"
310 db.replace("nsr_lcm", {"id": nsr_id}, nsr_lcm)
311
312 return nsr_lcm
313
314 except (ROclient.ROClientException, Exception) as e:
315 logger.debug("CreateNS nsr_id={} Exception {}".format(nsr_id, e), exc_info=True)
316 nsr_lcm["status"] = "ERROR"
317 nsr_lcm["status_detailed"] += ": ERROR {}".format(e)
318 finally:
319 logger.debug("CreateNS task nsr_id={} Exit".format(nsr_id))
320
321
322 async def DestroyNS(loop, nsr_id):
323 logger.debug("DestroyNS task nsr_id={} Enter".format(nsr_id))
324 nsr_lcm = db.get_one("nsr_lcm", {"id": nsr_id})
325 ns_request = db.get_one("ns_request", {"id": nsr_id})
326 nsd_id = ns_request["nsd_id"]
327
328 nsr_lcm["status"] = "DELETING"
329 nsr_lcm["status_detailed"] = "Deleting charms"
330 db.replace("nsr_lcm", {"id": nsr_id}, nsr_lcm)
331
332 # TODO destroy charms
333 for c_vnf in nsd["constituent-vnfd"]:
334 vnfd_id = c_vnf["vnfd-id-ref"]
335 vnfd_index = int(c_vnf["member-vnf-index"])
336 vnfd = db.get_one("vnfd", {"id": vnfd_id})
337 if vnfd.get("vnf-configuration") and vnfd["vnf-configuration"].get("juju"):
338 RemoveApplication(
339 get_vnf_unique_name(
340 nsd_id, vnfd_id, vnfd_index
341 )
342 )
343
344 # remove from RO
345 RO = ROclient.ROClient(loop, endpoint_url=ro_account["url"], tenant=ro_account["tenant"],
346 datacenter=ns_request["vim"])
347 # Delete ns
348 try:
349 RO_nsr_id = nsr_lcm["RO"]["nsr_id"]
350 if RO_nsr_id:
351 nsr_lcm["status_detailed"] = "Deleting ns at RO"
352 desc = await RO.delete("ns", RO_nsr_id)
353 print("debug", "deleted RO ns {}".format(RO_nsr_id))
354 nsr_lcm["RO"]["nsr_id"] = None
355 nsr_lcm["RO"]["nsr_status"] = "DELETED"
356 db.replace("nsr_lcm", {"id": nsr_id}, nsr_lcm)
357 except ROclient.ROClientException as e:
358 if e.http_code == 404:
359 nsr_lcm["RO"]["nsr_id"] = None
360 nsr_lcm["RO"]["nsr_status"] = "DELETED"
361 db.replace("nsr_lcm", {"id": nsr_id}, nsr_lcm)
362 print("warning", e)
363 else:
364 print("error", e)
365
366 # Delete nsd
367 try:
368 RO_nsd_id = nsr_lcm["RO"]["nsd_id"]
369 if RO_nsd_id:
370 nsr_lcm["status_detailed"] = "Deleting nsd at RO"
371 desc = await RO.delete("nsd", RO_nsd_id)
372 print("debug", "deleted RO nsd {}".format(RO_nsd_id))
373 nsr_lcm["RO"]["nsd_id"] = None
374 db.replace("nsr_lcm", {"id": nsr_id}, nsr_lcm)
375 except ROclient.ROClientException as e:
376 if e.http_code == 404:
377 nsr_lcm["RO"]["nsd_id"] = None
378 print("warning", e)
379 else:
380 print("error", e)
381
382 for vnf_id, RO_vnfd_id in nsr_lcm["RO"]["vnfd_id"].items():
383 try:
384 if RO_vnfd_id:
385 nsr_lcm["status_detailed"] = "Deleting vnfd at RO"
386 desc = await RO.delete("vnfd", RO_vnfd_id)
387 print("debug", "deleted RO vnfd {}".format(RO_vnfd_id))
388 nsr_lcm["RO"]["vnfd_id"][vnf_id] = None
389 db.replace("nsr_lcm", {"id": nsr_id}, nsr_lcm)
390 except ROclient.ROClientException as e:
391 if e.http_code == 404:
392 nsr_lcm["RO"]["vnfd_id"][vnf_id] = None
393 print("warning", e)
394 else:
395 print("error", e)
396 logger.debug("DestroyNS task nsr_id={} Exit".format(nsr_id))
397
398
399 async def test(loop, param=None):
400 logger.debug("Starting/Ending test task: {}".format(param))
401
402
403 def cancel_tasks(loop, nsr_id):
404 """
405 Cancel all active tasks of a concrete nsr identified for nsr_id
406 :param loop: loop
407 :param nsr_id: nsr identity
408 :return: None, or raises an exception if not possible
409 """
410 global lcm_tasks
411 if not lcm_tasks.get(nsr_id):
412 return
413 for order_id, tasks_set in lcm_tasks[nsr_id].items():
414 for task_name, task in tasks_set.items():
415 result = task.cancel()
416 if result:
417 logger.debug("nsr_id={} order_id={} task={} cancelled".format(nsr_id, order_id, task_name))
418 lcm_tasks[nsr_id] = {}
419
420
421 async def read_kafka(loop, bus_info):
422 global lcm_tasks
423 logger.debug("kafka task Enter")
424 order_id = 1
425 # future = asyncio.Future()
426 with open(bus_info["file"]) as f:
427
428 # ignore old orders. Read file
429 command = "fake"
430 while command:
431 command = f.read()
432
433 while True:
434 command = f.read()
435 if not command:
436 await asyncio.sleep(2, loop=loop)
437 continue
438 order_id += 1
439 command = command.strip()
440 command, _, params = command.partition(" ")
441 if command == "exit":
442 print("Bye!")
443 break
444 elif command.startswith("#"):
445 continue
446 elif command == "echo":
447 print(params)
448 elif command == "test":
449 asyncio.Task(test(loop, params), loop=loop)
450 elif command == "break":
451 print("put a break in this line of code")
452 elif command == "new-ns":
453 nsr_id = params.strip()
454 logger.debug("Deploying NS {}".format(nsr_id))
455 task = asyncio.ensure_future(CreateNS(loop, nsr_id))
456 if nsr_id not in lcm_tasks:
457 lcm_tasks[nsr_id] = {}
458 lcm_tasks[nsr_id][order_id] = {"CreateNS": task}
459 elif command == "del-ns":
460 nsr_id = params.strip()
461 logger.debug("Deleting NS {}".format(nsr_id))
462 cancel_tasks(loop, nsr_id)
463 task = asyncio.ensure_future(DestroyNS(loop, nsr_id))
464 if nsr_id not in lcm_tasks:
465 lcm_tasks[nsr_id] = {}
466 lcm_tasks[nsr_id][order_id] = {"DestroyNS": task}
467 elif command == "get-ns":
468 nsr_id = params.strip()
469 nsr_lcm = db.get_one("nsr_lcm", {"id": nsr_id})
470 print("nsr_lcm", nsr_lcm)
471 print("lcm_tasks", lcm_tasks.get(nsr_id))
472 else:
473 logger.debug("unknown command '{}'".format(command))
474 print("Usage:\n echo <>\n new-ns <ns1|ns2>\n del-ns <ns1|ns2>\n get-ns <ns1|ns2>")
475 logger.debug("kafka task Exit")
476
477
478 def lcm(kafka):
479 loop = asyncio.get_event_loop()
480 loop.run_until_complete(read_kafka(loop, {"file": kafka}))
481 return
482
483
484 def lcm2():
485 loop = asyncio.get_event_loop()
486 # asyncio.ensure_future(CreateNS, loop)
487 try:
488 content = loop.run_until_complete(CreateNS(loop, "ns1"))
489 print("Done: {}".format(content))
490 except ROclient.ROClientException as e:
491 print("Error {}".format(e))
492
493 time.sleep(10)
494
495 content = loop.run_until_complete(DestroyNS(loop, "ns1"))
496 print(content)
497
498 loop.close()
499
500
501 def get_argparser():
502 parser = argparse.ArgumentParser()
503
504 parser.add_argument(
505 '--vnfd',
506 nargs="+",
507 type=str,
508 action='append',
509 required=True,
510 )
511 parser.add_argument(
512 '--nsd',
513 type=str,
514 required=True,
515 )
516
517 parser.add_argument(
518 '--kafka',
519 type=str,
520 required=True,
521 )
522
523 parser.add_argument(
524 '--datacenter',
525 type=str,
526 required=True,
527 default="OST2_MRT"
528 )
529 args = parser.parse_args()
530
531 # Quick hack to make this one list
532 vnfds = []
533 for vnfd in args.vnfd:
534 vnfds += vnfd
535 args.vnfd = vnfds
536
537 return args
538
539
540 # def find_yaml(path):
541 # """Find the first yaml file, rescursively, in the path."""
542 # for filename in glob.iglob('path/**/.yaml'):
543 # print(filename)
544 #
545 #
546 if __name__ == '__main__':
547
548 args = get_argparser()
549 print(args)
550
551 # FOR TEST
552 RO_VIM = args.datacenter
553
554 # Unpack the NSD/VNFD packages to a persistent on-disk cache
555 if os.path.exists('.cache'):
556 shutil.rmtree('.cache')
557 os.mkdir('.cache')
558
559 for vnfd in args.vnfd:
560 if mimetypes.guess_type(vnfd)[0] == "application/x-tar":
561 with tarfile.open(vnfd) as tar:
562 tar.extractall('.cache/')
563 # The path is the root of our charm
564 vnfd_dir = "{}/.cache/{}".format(
565 os.path.dirname(
566 os.path.realpath(__file__)
567 ),
568 tar.getnames()[0]
569 )
570 for entity in tar:
571 if entity.name.endswith('_vnfd.yaml'):
572 print("VNFD: {}/{}".format(".cache", entity.name))
573 with open("{}/{}".format(".cache", entity.name)) as f:
574 vnfd = yaml.load(f)
575 vnfd_clean, _ = ROclient.remove_envelop("vnfd", vnfd)
576 vnfd_clean["_admin"] = {"storage": vnfd_dir}
577 db.create("vnfd", vnfd_clean)
578
579 if mimetypes.guess_type(args.nsd)[0] == "application/x-tar":
580 with tarfile.open(args.nsd) as tar:
581 tar.extractall('.cache/')
582
583 nsd_dir = "{}/.cache/{}".format(
584 os.path.dirname(
585 os.path.realpath(__file__)
586 ),
587 tar.getnames()[0]
588 )
589 for entity in tar:
590 if entity.name.endswith('_nsd.yaml'):
591 with open("{}/{}".format(".cache", entity.name)) as f:
592 nsd = yaml.load(f)
593 nsd_clean, _ = ROclient.remove_envelop("nsd", nsd)
594 nsd_clean["_admin"] = {"storage": nsd_dir}
595 db.create("nsd", nsd_clean)
596
597 ns_request = {
598 "id": "ns1",
599 "nsr_id": "ns1",
600 "name": "pingpongOne",
601 "vim": RO_VIM,
602 "nsd_id": nsd_clean["id"], # nsd_ping_pong
603 }
604 db.create("ns_request", ns_request)
605 ns_request = {
606 "id": "ns2",
607 "nsr_id": "ns2",
608 "name": "pingpongTwo",
609 "vim": RO_VIM,
610 "nsd_id": nsd_clean["id"], # nsd_ping_pong
611 }
612 db.create("ns_request", ns_request)
613 # lcm2()
614 lcm(args.kafka)
615
616 pass
617
618 #FILL DATABASE
619 # with open("/home/atierno/OSM/osm/devops/descriptor-packages/vnfd/ping_vnf/src/ping_vnfd.yaml") as f:
620 # vnfd = yaml.load(f)
621 # vnfd_clean, _ = ROclient.remove_envelop("vnfd", vnfd)
622 # vnfd_clean["_admin"] = {"storage": "/home/atierno/OSM/osm/devops/descriptor-packages/vnfd/ping_vnf"}
623 # db.create("vnfd", vnfd_clean)
624 # with open("/home/atierno/OSM/osm/devops/descriptor-packages/vnfd/pong_vnf/src/pong_vnfd.yaml") as f:
625 # vnfd = yaml.load(f)
626 # vnfd_clean, _ = ROclient.remove_envelop("vnfd", vnfd)
627 # vnfd_clean["_admin"] = {"storage": "/home/atierno/OSM/osm/devops/descriptor-packages/vnfd/pong_vnf"}
628 # db.create("vnfd", vnfd_clean)
629 # with open("/home/atierno/OSM/osm/devops/descriptor-packages/nsd/ping_pong_ns/src/ping_pong_nsd.yaml") as f:
630 # nsd = yaml.load(f)
631 # nsd_clean, _ = ROclient.remove_envelop("nsd", nsd)
632 # nsd_clean["_admin"] = {"storage": "/home/atierno/OSM/osm/devops/descriptor-packages/nsd/ping_pong_ns"}
633 # db.create("nsd", nsd_clean)
634 #
635 # ns_request = {
636 # "id": "ns1",
637 # "nsr_id": "ns1",
638 # "name": "pingpongOne",
639 # "vim": RO_VIM,
640 # "nsd_id": nsd_clean["id"], # nsd_ping_pong
641 # }
642 # db.create("ns_request", ns_request)
643 # ns_request = {
644 # "id": "ns2",
645 # "nsr_id": "ns2",
646 # "name": "pingpongTwo",
647 # "vim": RO_VIM,
648 # "nsd_id": nsd_clean["id"], # nsd_ping_pong
649 # }
650 # db.create("ns_request", ns_request)
651 # # lcm2()
652 # lcm()