3 # Copyright 2016 RIFT.IO Inc
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
20 import ncclient
.asyncio_manager
21 import tornado
.httpclient
as tornadoh
22 import asyncio
.subprocess
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')
35 from gi
.repository
import (
38 RwConmanYang
as conmanY
,
45 if sys
.version_info
< (3, 4, 4):
46 asyncio
.ensure_future
= asyncio
.async
48 def log_this_vnf(vnf_cfg
):
50 used_item_list
= ['nsr_name', 'vnfr_name', 'member_vnf_index', 'mgmt_ip_address']
51 for item
in used_item_list
:
53 if item
== 'mgmt_ip_address':
54 log_vnf
+= "({})".format(vnf_cfg
[item
])
56 log_vnf
+= "{}/".format(vnf_cfg
[item
])
60 class ConfigManagerROifConnectionError(Exception):
64 class ScriptError(Exception):
68 class ConfigManagerEvents(object):
69 def __init__(self
, dts
, log
, loop
, parent
):
74 self
._nsr
_xpath
= parent
._project
.add_project("/cm-state/cm-nsr")
84 def update_vnf_state(self
, vnf_cfg
, state
):
85 nsr_obj
= vnf_cfg
['nsr_obj']
86 yield from nsr_obj
.update_vnf_cm_state(vnf_cfg
['vnfr'], state
)
89 def apply_vnf_config(self
, vnf_cfg
):
90 self
._log
.debug("apply_vnf_config VNF:{}"
91 .format(log_this_vnf(vnf_cfg
)))
93 if vnf_cfg
['config_delay']:
94 yield from self
.update_vnf_state(vnf_cfg
, conmanY
.RecordState
.CFG_DELAY
)
95 yield from asyncio
.sleep(vnf_cfg
['config_delay'], loop
=self
._loop
)
97 # See if we are still alive!
98 if vnf_cfg
['nsr_obj'].being_deleted
:
99 # Don't do anything, just return
100 self
._log
.info("VNF : %s is being deleted, skipping configuration!",
101 log_this_vnf(vnf_cfg
))
104 yield from self
.update_vnf_state(vnf_cfg
, conmanY
.RecordState
.CFG_SEND
)
106 if vnf_cfg
['config_method'] == 'netconf':
107 self
._log
.info("Creating ncc handle for VNF cfg = %s!", vnf_cfg
)
108 self
.ncc
= ConfigManagerVNFnetconf(self
._log
, self
._loop
, self
, vnf_cfg
)
109 if vnf_cfg
['protocol'] == 'ssh':
110 yield from self
.ncc
.connect_ssh()
112 yield from self
.ncc
.connect()
113 yield from self
.ncc
.apply_edit_cfg()
114 elif vnf_cfg
['config_method'] == 'rest':
116 self
._log
.info("Creating rcc handle for VNF cfg = %s!", vnf_cfg
)
117 self
.rcc
= ConfigManagerVNFrestconf(self
._log
, self
._loop
, self
, vnf_cfg
)
118 self
.ncc
.apply_edit_cfg()
119 elif vnf_cfg
['config_method'] == 'script':
120 self
._log
.info("Executing script for VNF cfg = %s!", vnf_cfg
)
121 scriptc
= ConfigManagerVNFscriptconf(self
._log
, self
._loop
, self
, vnf_cfg
)
122 yield from scriptc
.apply_edit_cfg()
123 elif vnf_cfg
['config_method'] == 'juju':
124 self
._log
.info("Executing juju config for VNF cfg = %s!", vnf_cfg
)
125 jujuc
= ConfigManagerVNFjujuconf(self
._log
, self
._loop
, self
._parent
, vnf_cfg
)
126 yield from jujuc
.apply_edit_cfg()
128 self
._log
.error("Unknown configuration method(%s) received for %s",
129 vnf_cfg
['config_method'], vnf_cfg
['vnf_unique_name'])
130 yield from self
.update_vnf_state(vnf_cfg
, conmanY
.RecordState
.CFG_FAILED
)
134 yield from self
.update_vnf_state(vnf_cfg
, conmanY
.RecordState
.READY
)
135 self
._log
.info("Successfully applied configuration to VNF: %s",
136 log_this_vnf(vnf_cfg
))
137 except Exception as e
:
138 self
._log
.error("Applying configuration(%s) file(%s) to VNF: %s failed as: %s",
139 vnf_cfg
['config_method'],
141 log_this_vnf(vnf_cfg
),
148 class ConfigManagerVNFscriptconf(object):
150 def __init__(self
, log
, loop
, parent
, vnf_cfg
):
153 self
._parent
= parent
155 self
._vnf
_cfg
= vnf_cfg
158 def apply_edit_cfg(self
):
159 vnf_cfg
= self
._vnf
_cfg
160 self
._log
.debug("Attempting to apply scriptconf to VNF: %s", log_this_vnf(vnf_cfg
))
162 st
= os
.stat(vnf_cfg
['cfg_file'])
163 os
.chmod(vnf_cfg
['cfg_file'], st
.st_mode | stat
.S_IEXEC
)
164 #script_msg = subprocess.check_output(vnf_cfg['cfg_file'], shell=True).decode('utf-8')
166 proc
= yield from asyncio
.create_subprocess_exec(
167 vnf_cfg
['script_type'], vnf_cfg
['cfg_file'],
168 stdout
=asyncio
.subprocess
.PIPE
)
169 script_msg
= yield from proc
.stdout
.read()
170 rc
= yield from proc
.wait()
174 "script config returned error code : %s" % rc
177 self
._log
.debug("config script output (%s)", script_msg
)
178 except Exception as e
:
179 self
._log
.error("Error (%s) while executing script config for VNF: %s",
180 str(e
), log_this_vnf(vnf_cfg
))
183 class ConfigManagerVNFrestconf(object):
185 def __init__(self
, log
, loop
, parent
, vnf_cfg
):
188 self
._parent
= parent
190 self
._vnf
_cfg
= vnf_cfg
192 def fetch_handle(self
, response
):
194 self
._log
.error("Failed to send HTTP config request - %s", response
.error
)
196 self
._log
.debug("Sent HTTP config request - %s", response
.body
)
199 def apply_edit_cfg(self
):
200 vnf_cfg
= self
._vnf
_cfg
201 self
._log
.debug("Attempting to apply restconf to VNF: %s", log_this_vnf(vnf_cfg
))
203 http_c
= tornadoh
.AsyncHTTPClient()
205 # Read the config entity from file?
206 # Convert connectoin-point?
207 http_c
.fetch("http://", self
.fetch_handle
)
208 except Exception as e
:
209 self
._log
.error("Error (%s) while applying HTTP config", str(e
))
211 class ConfigManagerVNFnetconf(object):
213 def __init__(self
, log
, loop
, parent
, vnf_cfg
):
216 self
._parent
= parent
218 self
._vnf
_cfg
= vnf_cfg
220 self
._model
= RwYang
.Model
.create_libncx()
221 self
._model
.load_schema_ypbc(conmanY
.get_schema())
224 def connect(self
, timeout_secs
=120):
225 vnf_cfg
= self
._vnf
_cfg
226 start_time
= time
.time()
227 self
._log
.debug("connecting netconf .... %s", vnf_cfg
)
228 while (time
.time() - start_time
) < timeout_secs
:
231 self
._log
.info("Attemping netconf connection to VNF: %s", log_this_vnf(vnf_cfg
))
233 self
._manager
= yield from ncclient
.asyncio_manager
.asyncio_connect(
235 host
=vnf_cfg
['mgmt_ip_address'],
236 port
=vnf_cfg
['port'],
237 username
=vnf_cfg
['username'],
238 password
=vnf_cfg
['password'],
241 hostkey_verify
=False,
244 self
._log
.info("Netconf connected to VNF: %s", log_this_vnf(vnf_cfg
))
247 except ncclient
.transport
.errors
.SSHError
as e
:
248 yield from self
._parent
.update_vnf_state(vnf_cfg
, conmanY
.RecordState
.FAILED_CONNECTION
)
249 self
._log
.error("Netconf connection to VNF: %s, failed: %s",
250 log_this_vnf(vnf_cfg
), str(e
))
252 yield from asyncio
.sleep(2, loop
=self
._loop
)
254 raise ConfigManagerROifConnectionError(
255 "Failed to connect to VNF: %s within %s seconds" %
256 (log_this_vnf(vnf_cfg
), timeout_secs
)
260 def connect_ssh(self
, timeout_secs
=120):
261 vnf_cfg
= self
._vnf
_cfg
262 start_time
= time
.time()
264 if (self
._manager
!= None and self
._manager
.connected
== True):
265 self
._log
.debug("Disconnecting previous session")
266 self
._manager
.close_session
268 self
._log
.debug("connecting netconf via SSH .... %s", vnf_cfg
)
269 while (time
.time() - start_time
) < timeout_secs
:
272 yield from self
._parent
.update_vnf_state(vnf_cfg
, conmanY
.RecordState
.CONNECTING
)
273 self
._log
.debug("Attemping netconf connection to VNF: %s", log_this_vnf(vnf_cfg
))
275 self
._manager
= ncclient
.asyncio_manager
.manager
.connect_ssh(
276 host
=vnf_cfg
['mgmt_ip_address'],
277 port
=vnf_cfg
['port'],
278 username
=vnf_cfg
['username'],
279 password
=vnf_cfg
['password'],
282 hostkey_verify
=False,
285 yield from self
._parent
.update_vnf_state(vnf_cfg
, conmanY
.RecordState
.NETCONF_SSH_CONNECTED
)
286 self
._log
.debug("netconf over SSH connected to VNF: %s", log_this_vnf(vnf_cfg
))
289 except ncclient
.transport
.errors
.SSHError
as e
:
290 yield from self
._parent
.update_vnf_state(vnf_cfg
, conmanY
.RecordState
.FAILED_CONNECTION
)
291 self
._log
.error("Netconf connection to VNF: %s, failed: %s",
292 log_this_vnf(vnf_cfg
), str(e
))
294 yield from asyncio
.sleep(2, loop
=self
._loop
)
296 raise ConfigManagerROifConnectionError(
297 "Failed to connect to VNF: %s within %s seconds" %
298 (log_this_vnf(vnf_cfg
), timeout_secs
)
302 def apply_edit_cfg(self
):
303 vnf_cfg
= self
._vnf
_cfg
304 self
._log
.debug("Attempting to apply netconf to VNF: %s", log_this_vnf(vnf_cfg
))
306 if self
._manager
is None:
307 self
._log
.error("Netconf is not connected to VNF: %s, aborting!", log_this_vnf(vnf_cfg
))
310 # Get config file contents
312 with
open(vnf_cfg
['cfg_file']) as f
:
313 configuration
= f
.read()
314 except Exception as e
:
315 self
._log
.error("Reading contents of the configuration file(%s) failed: %s", vnf_cfg
['cfg_file'], str(e
))
319 self
._log
.debug("apply_edit_cfg to VNF: %s", log_this_vnf(vnf_cfg
))
320 xml
= '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">{}</config>'.format(configuration
)
321 response
= yield from self
._manager
.edit_config(xml
, target
='running')
322 if hasattr(response
, 'xml'):
323 response_xml
= response
.xml
325 response_xml
= response
.data_xml
.decode()
327 self
._log
.debug("apply_edit_cfg response: %s", response_xml
)
328 if '<rpc-error>' in response_xml
:
329 raise ConfigManagerROifConnectionError("apply_edit_cfg response has rpc-error : %s",
332 self
._log
.debug("apply_edit_cfg Successfully applied configuration {%s}", xml
)
336 class ConfigManagerVNFjujuconf(object):
338 def __init__(self
, log
, loop
, parent
, vnf_cfg
):
341 self
._parent
= parent
343 self
._vnf
_cfg
= vnf_cfg
346 def apply_edit_cfg(self
):
347 vnf_cfg
= self
._vnf
_cfg
348 self
._log
.debug("Attempting to apply juju conf to VNF: %s", log_this_vnf(vnf_cfg
))
351 vnf_cfg
['juju_script'],
352 '--server', vnf_cfg
['mgmt_ip_address'],
353 '--user', vnf_cfg
['user'],
354 '--password', vnf_cfg
['secret'],
355 '--port', str(vnf_cfg
['port']),
357 self
._log
.error("juju script command (%s)", args
)
359 proc
= yield from asyncio
.create_subprocess_exec(
361 stdout
=asyncio
.subprocess
.PIPE
)
362 juju_msg
= yield from proc
.stdout
.read()
363 rc
= yield from proc
.wait()
367 "Juju config returned error code : %s" % rc
370 self
._log
.debug("Juju config output (%s)", juju_msg
)
371 except Exception as e
:
372 self
._log
.error("Error (%s) while executing juju config", str(e
))