lightweight exception capturing, logging
[osm/RO.git] / lcm / osm_lcm / vca.py
1 #!/usr/bin/python3
2 # -*- coding: utf-8 -*-
3
4 from juju_api import JujuApi
5 from juju.model import ModelObserver
6 import logging
7 import os
8 import os.path
9 import re
10
11
12 class VCAMonitor(ModelObserver):
13 """Monitor state changes within the Juju Model."""
14 context = None
15
16 async def on_change(self, delta, old, new, model):
17 """React to changes in the Juju model."""
18 status = None
19 db_nsr = self.context['db_nsr']
20 vnf_index = self.context['vnf_index']
21
22 nsr_lcm = db_nsr["_admin"]["deploy"]
23 nsr_id = nsr_lcm["id"]
24 application = self.context['application']
25
26 if delta.entity == "unit":
27 # We only care about changes to a unit
28 if delta.type == "add" and old is None:
29 if new and new.application == application:
30 status = "BUILD"
31 elif delta.type == "change":
32 if new and new.application == application:
33 if new.agent_status == "idle":
34 if new.workload_status in ("active", "blocked"):
35 status = "ACTIVE"
36
37 elif delta.type == "remove" and new is None:
38 if new and new.application == application:
39 status = "DELETING"
40
41 if status:
42 nsr_lcm["VCA"][vnf_index]['operational-status'] = status
43
44 # TODO: Clean this up, and make it work with deletes (if we need
45 # TODO: to update the database post-delete)
46 # Figure out if we're finished configuring
47 count = len(nsr_lcm["VCA"])
48 active = 0
49 for vnf_index in nsr_lcm["VCA"]:
50 if nsr_lcm["VCA"][vnf_index]['operational-status'] == "ACTIVE":
51 active += 1
52 if active == count:
53 db_nsr["config-status"] = "done"
54 else:
55 db_nsr["config-status"] = "configuring {}/{}".format(active, count)
56
57 try:
58 self.context['db'].replace(
59 "nsrs",
60 nsr_id,
61 db_nsr
62 )
63
64 # self.context['db'].replace(
65 # "nsr_lcm",
66 # {"id": self.context['nsr_lcm']['id']},
67 # self.context['nsr_lcm']
68 # )
69 except Exception as e:
70 # I've seen this happen when we handle a delete, because the
71 # db record is gone by the time we've finished deleting
72 # the charms.
73 print("Error updating database: ", e)
74
75 pass
76
77
78 def GetJujuApi(config):
79 # Quiet logging from the websocket library. If you want to see
80 # everything sent over the wire, set this to DEBUG.
81
82 ws_logger = logging.getLogger('websockets.protocol')
83 ws_logger.setLevel(logging.INFO)
84
85 api = JujuApi(server=config['host'],
86 port=config['port'],
87 user=config['user'],
88 secret=config['secret'],
89 log=ws_logger,
90 model_name='default'
91 )
92 return api
93
94
95 def get_vnf_unique_name(nsr_name, vnfr_name, member_vnf_index):
96 """Get the unique VNF name.
97 Charm names accepts only a to z and non-consecutive - characters."""
98 name = "{}-{}-{}".format(nsr_name, vnfr_name, member_vnf_index)
99 new_name = ''
100 for c in name:
101 if c.isdigit():
102 c = chr(97 + int(c))
103 elif not c.isalpha():
104 c = "-"
105 new_name += c
106 return re.sub('\-+', '-', new_name.lower())
107
108
109 def get_initial_config(initial_config_primitive, mgmt_ip):
110 config = {}
111 for primitive in initial_config_primitive:
112 if primitive['name'] == 'config':
113 for parameter in primitive['parameter']:
114 param = parameter['name']
115 if parameter['value'] == "<rw_mgmt_ip>":
116 config[param] = mgmt_ip
117 else:
118 config[param] = parameter['value']
119 return config
120
121
122 async def DeployApplication(vcaconfig, db, db_nsr, vnfd,
123 vnf_index, charm_path):
124 """
125 Deploy a charm.
126
127 Deploy a VNF configuration charm from a local directory.
128 :param dict vcaconfig: The VCA portion of the LCM Configuration
129 :param object vnfd: The VNF descriptor
130 ...
131 :param int vnfd_index: The index of the vnf.
132
133 :Example:
134
135 DeployApplication(...)
136 """
137 nsr_lcm = db_nsr["_admin"]["deploy"]
138 nsr_id = db_nsr["_id"]
139 vnf_id = vnfd['id']
140
141 if "proxy" in vnfd["vnf-configuration"]["juju"]:
142 use_proxy = vnfd["vnf-configuration"]["juju"]["proxy"]
143 else:
144 # TBD: We need this to handle a full charm
145 use_proxy = True
146
147 application = get_vnf_unique_name(
148 db_nsr["name"].lower().strip(),
149 vnf_id,
150 vnf_index,
151 )
152
153 api = GetJujuApi(vcaconfig)
154
155 await api.login()
156 if api.authenticated:
157 charm = os.path.basename(charm_path)
158
159 # Set the INIT state; further operational status updates
160 # will be made by the VCAMonitor
161 nsr_lcm["VCA"][vnf_index] = {}
162 nsr_lcm["VCA"][vnf_index]['operational-status'] = 'INIT'
163 nsr_lcm["VCA"][vnf_index]['application'] = application
164
165 db.replace("nsrs", nsr_id, db_nsr)
166
167 model = await api.get_model()
168 context = {
169 'application': application,
170 'vnf_index': vnf_index,
171 'db_nsr': db_nsr,
172 'db': db,
173 }
174 mon = VCAMonitor()
175 mon.context = context
176 model.add_observer(mon)
177
178 await api.deploy_application(charm,
179 name=application,
180 path=charm_path,
181 )
182
183 # Get and apply the initial config primitive
184 cfg = get_initial_config(
185 vnfd["vnf-configuration"].get(
186 "initial-config-primitive"
187 ),
188 nsr_lcm['nsr_ip'][vnf_index]
189 )
190
191 await api.apply_config(cfg, application)
192
193 await api.logout()
194
195
196 async def RemoveApplication(vcaconfig, db, db_nsr, vnf_index):
197 """
198 Remove an application from the Juju Controller
199
200 Removed the named application and it's charm from the Juju controller.
201
202 :param object loop: The event loop.
203 :param str application_name: The unique name of the application.
204
205 :Example:
206
207 RemoveApplication(loop, "ping_vnf")
208 RemoveApplication(loop, "pong_vnf")
209 """
210 nsr_id = db_nsr["_id"]
211 nsr_lcm = db_nsr["_admin"]["deploy"]
212 application = nsr_lcm["VCA"][vnf_index]['application']
213
214 api = GetJujuApi(vcaconfig)
215
216 await api.login()
217 if api.authenticated:
218 model = await api.get_model()
219 context = {
220 'application': application,
221 'vnf_index': vnf_index,
222 'db_nsr': db_nsr,
223 'db': db,
224 }
225
226 mon = VCAMonitor()
227 mon.context = context
228 model.add_observer(mon)
229
230 print("VCA: Removing application {}".format(application))
231 await api.remove_application(application)
232 nsr_lcm["VCA"][vnf_index]['application'] = None
233 db.replace("nsrs", nsr_id, db_nsr)
234 await api.logout()