2 # -*- coding: utf-8 -*-
7 from juju_api
import JujuApi
23 from copy
import deepcopy
24 from uuid
import uuid4
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')
32 "url": "http://10.105.129.121:9090/openmano",
37 "ip": "10.105.129.40",
40 "secret": "NzdjM2M4ODA5NjlhNzRkZGJhMzc2NjNk",
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
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
)
53 ws_logger
= logging
.getLogger('websockets.protocol')
54 ws_logger
.setLevel(logging
.INFO
)
56 api
= JujuApi(server
=vca_account
['ip'],
57 port
=vca_account
['port'],
58 user
=vca_account
['user'],
59 secret
=vca_account
['secret'],
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
)
78 return re
.sub('\-+', '-', new_name
.lower())
81 async def DeployApplication(loop
, application_name
, charm_path
, config
):
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.
92 DeployApplication(loop, ".cache/ping_vnf/charm/pingpong", "ping_vnf")
95 api
= GetJujuApi(loop
)
99 charm
= os
.path
.basename(charm_path
)
101 await api
.deploy_application(charm
,
102 name
=application_name
,
105 await api
.apply_config(config
, application_name
)
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))
113 # Deploy the charm and wait, periodically checking its status
114 await api
.wait_for_application(charm
, 30)
116 error
= await api
.is_application_error(charm
)
118 print("This application is in an error state.")
121 blocked
= await api
.is_application_blocked(charm
)
123 print("This application is blocked.")
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"))
130 print("Application {} is deployed".format(args
.application
))
134 async def RemoveApplication(loop
, application_name
):
136 Remove an application from the Juju Controller
138 Removed the named application and it's charm from the Juju controller.
140 :param object loop: The event loop.
141 :param str application_name: The unique name of the application.
145 RemoveApplication(loop, "ping_vnf")
146 RemoveApplication(loop, "pong_vnf")
148 api
= GetJujuApi(loop
)
151 if api
.authenticated
:
152 print("Removing application {}".format(application_name
))
153 await api
.remove_application(application_name
)
156 # conains created tasks/futures to be able to cancel
159 headers_req
= {'Accept': 'application/yaml', 'content-type': 'application/yaml'}
160 ns_status
= ("CREATION-SCHEDULED", "DEPLOYING", "CONFIGURING", "DELETION-SCHEDULED", "UN-CONFIGURING", "UNDEPLOYING")
162 # TODO replace with database calls
163 db
= dbmemory
.dbmemory()
166 class RO_Exception(Exception):
170 async def CreateNS(loop
, nsr_id
):
171 logger
.debug("CreateNS task nsr_id={} Enter".format(nsr_id
))
174 "RO": {"vnfd_id": {}, "nsd_id": None, "nsr_id": None, "nsr_status": "SCHEDULED"},
178 "status_detailed": "",
181 deloyment_timeout
= 120
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"] = []
190 db
.create("nsr_lcm", nsr_lcm
)
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)
206 # TODO change id for RO in case it is present
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
217 # db_new("vnfr", vnfr)
218 # db_update("ns_request", nsr_id, ns_request)
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)
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
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
)
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
)
262 assert False, "ROclient.check_ns_status returns unknown {}".format(ns_status
)
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
)
271 #for nsd in nsr_lcm["descriptors"]["nsd"]:
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
]
287 # TODO launch VCA charm
288 # task = asyncio.ensure_future(DeployCharm(loop, path, mgmt_ip, config_primitive))
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
297 config
[param
] = parameter
['value']
299 task
= asyncio
.ensure_future(
302 get_vnf_unique_name(nsd_id
, vnfd_id
, vnfd_index
),
309 nsr_lcm
["status"] = "DONE"
310 db
.replace("nsr_lcm", {"id": nsr_id
}, nsr_lcm
)
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
)
319 logger
.debug("CreateNS task nsr_id={} Exit".format(nsr_id
))
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"]
328 nsr_lcm
["status"] = "DELETING"
329 nsr_lcm
["status_detailed"] = "Deleting charms"
330 db
.replace("nsr_lcm", {"id": nsr_id
}, nsr_lcm
)
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"):
340 nsd_id
, vnfd_id
, vnfd_index
345 RO
= ROclient
.ROClient(loop
, endpoint_url
=ro_account
["url"], tenant
=ro_account
["tenant"],
346 datacenter
=ns_request
["vim"])
349 RO_nsr_id
= nsr_lcm
["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
)
368 RO_nsd_id
= nsr_lcm
["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
382 for vnf_id
, RO_vnfd_id
in nsr_lcm
["RO"]["vnfd_id"].items():
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
396 logger
.debug("DestroyNS task nsr_id={} Exit".format(nsr_id
))
399 async def test(loop
, param
=None):
400 logger
.debug("Starting/Ending test task: {}".format(param
))
403 def cancel_tasks(loop
, nsr_id
):
405 Cancel all active tasks of a concrete nsr identified for nsr_id
407 :param nsr_id: nsr identity
408 :return: None, or raises an exception if not possible
411 if not lcm_tasks
.get(nsr_id
):
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()
417 logger
.debug("nsr_id={} order_id={} task={} cancelled".format(nsr_id
, order_id
, task_name
))
418 lcm_tasks
[nsr_id
] = {}
421 async def read_kafka(loop
, bus_info
):
423 logger
.debug("kafka task Enter")
425 # future = asyncio.Future()
426 with
open(bus_info
["file"]) as f
:
428 # ignore old orders. Read file
436 await asyncio
.sleep(2, loop
=loop
)
439 command
= command
.strip()
440 command
, _
, params
= command
.partition(" ")
441 if command
== "exit":
444 elif command
.startswith("#"):
446 elif command
== "echo":
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
))
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")
479 loop
= asyncio
.get_event_loop()
480 loop
.run_until_complete(read_kafka(loop
, {"file": kafka
}))
485 loop
= asyncio
.get_event_loop()
486 # asyncio.ensure_future(CreateNS, loop)
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
))
495 content
= loop
.run_until_complete(DestroyNS(loop
, "ns1"))
502 parser
= argparse
.ArgumentParser()
529 args
= parser
.parse_args()
531 # Quick hack to make this one list
533 for vnfd
in args
.vnfd
:
540 # def find_yaml(path):
541 # """Find the first yaml file, rescursively, in the path."""
542 # for filename in glob.iglob('path/**/.yaml'):
546 if __name__
== '__main__':
548 args
= get_argparser()
552 RO_VIM
= args
.datacenter
554 # Unpack the NSD/VNFD packages to a persistent on-disk cache
555 if os
.path
.exists('.cache'):
556 shutil
.rmtree('.cache')
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(
566 os
.path
.realpath(__file__
)
571 if entity
.name
.endswith('_vnfd.yaml'):
572 print("VNFD: {}/{}".format(".cache", entity
.name
))
573 with
open("{}/{}".format(".cache", entity
.name
)) as f
:
575 vnfd_clean
, _
= ROclient
.remove_envelop("vnfd", vnfd
)
576 vnfd_clean
["_admin"] = {"storage": vnfd_dir
}
577 db
.create("vnfd", vnfd_clean
)
579 if mimetypes
.guess_type(args
.nsd
)[0] == "application/x-tar":
580 with tarfile
.open(args
.nsd
) as tar
:
581 tar
.extractall('.cache/')
583 nsd_dir
= "{}/.cache/{}".format(
585 os
.path
.realpath(__file__
)
590 if entity
.name
.endswith('_nsd.yaml'):
591 with
open("{}/{}".format(".cache", entity
.name
)) as f
:
593 nsd_clean
, _
= ROclient
.remove_envelop("nsd", nsd
)
594 nsd_clean
["_admin"] = {"storage": nsd_dir
}
595 db
.create("nsd", nsd_clean
)
600 "name": "pingpongOne",
602 "nsd_id": nsd_clean
["id"], # nsd_ping_pong
604 db
.create("ns_request", ns_request
)
608 "name": "pingpongTwo",
610 "nsd_id": nsd_clean
["id"], # nsd_ping_pong
612 db
.create("ns_request", ns_request
)
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:
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)
638 # "name": "pingpongOne",
640 # "nsd_id": nsd_clean["id"], # nsd_ping_pong
642 # db.create("ns_request", ns_request)
646 # "name": "pingpongTwo",
648 # "nsd_id": nsd_clean["id"], # nsd_ping_pong
650 # db.create("ns_request", ns_request)