f292a689394a935a81fccf531ca982d08c710956
[osm/SO.git] / rwcm / plugins / rwconman / rift / tasklets / rwconmantasklet / rwconman_events.py
1
2 #
3 # Copyright 2016 RIFT.IO Inc
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17
18
19 import ncclient
20 import ncclient.asyncio_manager
21 import tornado.httpclient as tornadoh
22 import asyncio.subprocess
23 import asyncio
24 import time
25 import sys
26 import os, stat
27
28 import gi
29 gi.require_version('RwDts', '1.0')
30 gi.require_version('RwYang', '1.0')
31 gi.require_version('RwConmanYang', '1.0')
32 gi.require_version('RwNsrYang', '1.0')
33 gi.require_version('RwVnfrYang', '1.0')
34
35 from gi.repository import (
36 RwDts as rwdts,
37 RwYang,
38 RwConmanYang as conmanY,
39 RwNsrYang as nsrY,
40 RwVnfrYang as vnfrY,
41 )
42
43 import rift.tasklets
44
45 if sys.version_info < (3, 4, 4):
46 asyncio.ensure_future = asyncio.async
47
48 def log_this_vnf(vnf_cfg):
49 log_vnf = ""
50 used_item_list = ['nsr_name', 'vnfr_name', 'member_vnf_index', 'mgmt_ip_address']
51 for item in used_item_list:
52 if item in vnf_cfg:
53 if item == 'mgmt_ip_address':
54 log_vnf += "({})".format(vnf_cfg[item])
55 else:
56 log_vnf += "{}/".format(vnf_cfg[item])
57 return log_vnf
58
59 class ConfigManagerROifConnectionError(Exception):
60 pass
61 class ScriptError(Exception):
62 pass
63
64
65 class ConfigManagerEvents(object):
66 def __init__(self, dts, log, loop, parent):
67 self._dts = dts
68 self._log = log
69 self._loop = loop
70 self._parent = parent
71 self._nsr_xpath = "/cm-state/cm-nsr"
72
73 @asyncio.coroutine
74 def register(self):
75 pass
76
77 @asyncio.coroutine
78 def update_vnf_state(self, vnf_cfg, state):
79 nsr_obj = vnf_cfg['nsr_obj']
80 yield from nsr_obj.update_vnf_cm_state(vnf_cfg['vnfr'], state)
81
82 @asyncio.coroutine
83 def apply_vnf_config(self, vnf_cfg):
84 self._log.debug("apply_vnf_config VNF:{}"
85 .format(log_this_vnf(vnf_cfg)))
86
87 if vnf_cfg['config_delay']:
88 yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.CFG_DELAY)
89 yield from asyncio.sleep(vnf_cfg['config_delay'], loop=self._loop)
90
91 # See if we are still alive!
92 if vnf_cfg['nsr_obj'].being_deleted:
93 # Don't do anything, just return
94 self._log.info("VNF : %s is being deleted, skipping configuration!",
95 log_this_vnf(vnf_cfg))
96 return True
97
98 yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.CFG_SEND)
99 try:
100 if vnf_cfg['config_method'] == 'netconf':
101 self._log.info("Creating ncc handle for VNF cfg = %s!", vnf_cfg)
102 self.ncc = ConfigManagerVNFnetconf(self._log, self._loop, self, vnf_cfg)
103 if vnf_cfg['protocol'] == 'ssh':
104 yield from self.ncc.connect_ssh()
105 else:
106 yield from self.ncc.connect()
107 yield from self.ncc.apply_edit_cfg()
108 elif vnf_cfg['config_method'] == 'rest':
109 if self.rcc is None:
110 self._log.info("Creating rcc handle for VNF cfg = %s!", vnf_cfg)
111 self.rcc = ConfigManagerVNFrestconf(self._log, self._loop, self, vnf_cfg)
112 self.ncc.apply_edit_cfg()
113 elif vnf_cfg['config_method'] == 'script':
114 self._log.info("Executing script for VNF cfg = %s!", vnf_cfg)
115 scriptc = ConfigManagerVNFscriptconf(self._log, self._loop, self, vnf_cfg)
116 yield from scriptc.apply_edit_cfg()
117 elif vnf_cfg['config_method'] == 'juju':
118 self._log.info("Executing juju config for VNF cfg = %s!", vnf_cfg)
119 jujuc = ConfigManagerVNFjujuconf(self._log, self._loop, self._parent, vnf_cfg)
120 yield from jujuc.apply_edit_cfg()
121 else:
122 self._log.error("Unknown configuration method(%s) received for %s",
123 vnf_cfg['config_method'], vnf_cfg['vnf_unique_name'])
124 yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.CFG_FAILED)
125 return True
126
127 #Update VNF state
128 yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.READY)
129 self._log.info("Successfully applied configuration to VNF: %s",
130 log_this_vnf(vnf_cfg))
131 except Exception as e:
132 self._log.error("Applying configuration(%s) file(%s) to VNF: %s failed as: %s",
133 vnf_cfg['config_method'],
134 vnf_cfg['cfg_file'],
135 log_this_vnf(vnf_cfg),
136 str(e))
137 #raise
138 return False
139
140 return True
141
142 class ConfigManagerVNFscriptconf(object):
143
144 def __init__(self, log, loop, parent, vnf_cfg):
145 self._log = log
146 self._loop = loop
147 self._parent = parent
148 self._manager = None
149 self._vnf_cfg = vnf_cfg
150
151 #@asyncio.coroutine
152 def apply_edit_cfg(self):
153 vnf_cfg = self._vnf_cfg
154 self._log.debug("Attempting to apply scriptconf to VNF: %s", log_this_vnf(vnf_cfg))
155 try:
156 st = os.stat(vnf_cfg['cfg_file'])
157 os.chmod(vnf_cfg['cfg_file'], st.st_mode | stat.S_IEXEC)
158 #script_msg = subprocess.check_output(vnf_cfg['cfg_file'], shell=True).decode('utf-8')
159
160 proc = yield from asyncio.create_subprocess_exec(
161 vnf_cfg['script_type'], vnf_cfg['cfg_file'],
162 stdout=asyncio.subprocess.PIPE)
163 script_msg = yield from proc.stdout.read()
164 rc = yield from proc.wait()
165
166 if rc != 0:
167 raise ScriptError(
168 "script config returned error code : %s" % rc
169 )
170
171 self._log.debug("config script output (%s)", script_msg)
172 except Exception as e:
173 self._log.error("Error (%s) while executing script config for VNF: %s",
174 str(e), log_this_vnf(vnf_cfg))
175 raise
176
177 class ConfigManagerVNFrestconf(object):
178
179 def __init__(self, log, loop, parent, vnf_cfg):
180 self._log = log
181 self._loop = loop
182 self._parent = parent
183 self._manager = None
184 self._vnf_cfg = vnf_cfg
185
186 def fetch_handle(self, response):
187 if response.error:
188 self._log.error("Failed to send HTTP config request - %s", response.error)
189 else:
190 self._log.debug("Sent HTTP config request - %s", response.body)
191
192 @asyncio.coroutine
193 def apply_edit_cfg(self):
194 vnf_cfg = self._vnf_cfg
195 self._log.debug("Attempting to apply restconf to VNF: %s", log_this_vnf(vnf_cfg))
196 try:
197 http_c = tornadoh.AsyncHTTPClient()
198 # TBD
199 # Read the config entity from file?
200 # Convert connectoin-point?
201 http_c.fetch("http://", self.fetch_handle)
202 except Exception as e:
203 self._log.error("Error (%s) while applying HTTP config", str(e))
204
205 class ConfigManagerVNFnetconf(object):
206
207 def __init__(self, log, loop, parent, vnf_cfg):
208 self._log = log
209 self._loop = loop
210 self._parent = parent
211 self._manager = None
212 self._vnf_cfg = vnf_cfg
213
214 self._model = RwYang.Model.create_libncx()
215 self._model.load_schema_ypbc(conmanY.get_schema())
216
217 @asyncio.coroutine
218 def connect(self, timeout_secs=120):
219 vnf_cfg = self._vnf_cfg
220 start_time = time.time()
221 self._log.debug("connecting netconf .... %s", vnf_cfg)
222 while (time.time() - start_time) < timeout_secs:
223
224 try:
225 self._log.info("Attemping netconf connection to VNF: %s", log_this_vnf(vnf_cfg))
226
227 self._manager = yield from ncclient.asyncio_manager.asyncio_connect(
228 loop=self._loop,
229 host=vnf_cfg['mgmt_ip_address'],
230 port=vnf_cfg['port'],
231 username=vnf_cfg['username'],
232 password=vnf_cfg['password'],
233 allow_agent=False,
234 look_for_keys=False,
235 hostkey_verify=False,
236 )
237
238 self._log.info("Netconf connected to VNF: %s", log_this_vnf(vnf_cfg))
239 return
240
241 except ncclient.transport.errors.SSHError as e:
242 yield from self._parent.update_vnf_state(vnf_cfg, conmanY.RecordState.FAILED_CONNECTION)
243 self._log.error("Netconf connection to VNF: %s, failed: %s",
244 log_this_vnf(vnf_cfg), str(e))
245
246 yield from asyncio.sleep(2, loop=self._loop)
247
248 raise ConfigManagerROifConnectionError(
249 "Failed to connect to VNF: %s within %s seconds" %
250 (log_this_vnf(vnf_cfg), timeout_secs)
251 )
252
253 @asyncio.coroutine
254 def connect_ssh(self, timeout_secs=120):
255 vnf_cfg = self._vnf_cfg
256 start_time = time.time()
257
258 if (self._manager != None and self._manager.connected == True):
259 self._log.debug("Disconnecting previous session")
260 self._manager.close_session
261
262 self._log.debug("connecting netconf via SSH .... %s", vnf_cfg)
263 while (time.time() - start_time) < timeout_secs:
264
265 try:
266 yield from self._parent.update_vnf_state(vnf_cfg, conmanY.RecordState.CONNECTING)
267 self._log.debug("Attemping netconf connection to VNF: %s", log_this_vnf(vnf_cfg))
268
269 self._manager = ncclient.asyncio_manager.manager.connect_ssh(
270 host=vnf_cfg['mgmt_ip_address'],
271 port=vnf_cfg['port'],
272 username=vnf_cfg['username'],
273 password=vnf_cfg['password'],
274 allow_agent=False,
275 look_for_keys=False,
276 hostkey_verify=False,
277 )
278
279 yield from self._parent.update_vnf_state(vnf_cfg, conmanY.RecordState.NETCONF_SSH_CONNECTED)
280 self._log.debug("netconf over SSH connected to VNF: %s", log_this_vnf(vnf_cfg))
281 return
282
283 except ncclient.transport.errors.SSHError as e:
284 yield from self._parent.update_vnf_state(vnf_cfg, conmanY.RecordState.FAILED_CONNECTION)
285 self._log.error("Netconf connection to VNF: %s, failed: %s",
286 log_this_vnf(vnf_cfg), str(e))
287
288 yield from asyncio.sleep(2, loop=self._loop)
289
290 raise ConfigManagerROifConnectionError(
291 "Failed to connect to VNF: %s within %s seconds" %
292 (log_this_vnf(vnf_cfg), timeout_secs)
293 )
294
295 @asyncio.coroutine
296 def apply_edit_cfg(self):
297 vnf_cfg = self._vnf_cfg
298 self._log.debug("Attempting to apply netconf to VNF: %s", log_this_vnf(vnf_cfg))
299
300 if self._manager is None:
301 self._log.error("Netconf is not connected to VNF: %s, aborting!", log_this_vnf(vnf_cfg))
302 return
303
304 # Get config file contents
305 try:
306 with open(vnf_cfg['cfg_file']) as f:
307 configuration = f.read()
308 except Exception as e:
309 self._log.error("Reading contents of the configuration file(%s) failed: %s", vnf_cfg['cfg_file'], str(e))
310 return
311
312 try:
313 self._log.debug("apply_edit_cfg to VNF: %s", log_this_vnf(vnf_cfg))
314 xml = '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">{}</config>'.format(configuration)
315 response = yield from self._manager.edit_config(xml, target='running')
316 if hasattr(response, 'xml'):
317 response_xml = response.xml
318 else:
319 response_xml = response.data_xml.decode()
320
321 self._log.debug("apply_edit_cfg response: %s", response_xml)
322 if '<rpc-error>' in response_xml:
323 raise ConfigManagerROifConnectionError("apply_edit_cfg response has rpc-error : %s",
324 response_xml)
325
326 self._log.debug("apply_edit_cfg Successfully applied configuration {%s}", xml)
327 except:
328 raise
329
330 class ConfigManagerVNFjujuconf(object):
331
332 def __init__(self, log, loop, parent, vnf_cfg):
333 self._log = log
334 self._loop = loop
335 self._parent = parent
336 self._manager = None
337 self._vnf_cfg = vnf_cfg
338
339 #@asyncio.coroutine
340 def apply_edit_cfg(self):
341 vnf_cfg = self._vnf_cfg
342 self._log.debug("Attempting to apply juju conf to VNF: %s", log_this_vnf(vnf_cfg))
343 try:
344 args = ['python3',
345 vnf_cfg['juju_script'],
346 '--server', vnf_cfg['mgmt_ip_address'],
347 '--user', vnf_cfg['user'],
348 '--password', vnf_cfg['secret'],
349 '--port', str(vnf_cfg['port']),
350 vnf_cfg['cfg_file']]
351 self._log.error("juju script command (%s)", args)
352
353 proc = yield from asyncio.create_subprocess_exec(
354 *args,
355 stdout=asyncio.subprocess.PIPE)
356 juju_msg = yield from proc.stdout.read()
357 rc = yield from proc.wait()
358
359 if rc != 0:
360 raise ScriptError(
361 "Juju config returned error code : %s" % rc
362 )
363
364 self._log.debug("Juju config output (%s)", juju_msg)
365 except Exception as e:
366 self._log.error("Error (%s) while executing juju config", str(e))
367 raise