2 from ncclient import manager
3 from ncclient.transport.errors import AuthenticationError, SSHError
4 from lxml import etree as ET
5 from django.conf import settings
7 from django.core.cache import cache
9 FORMAT = '%(asctime)s %(levelname)s: %(message)s'
10 logging.basicConfig(format=FORMAT)
11 logger = logging.getLogger(__name__)
12 logger.setLevel(logging.DEBUG)
14 class Retriever(object):
15 def __init__(self, device=settings.NETCONF_DEVICE, username=settings.NETCONF_USER, password=settings.NETCONF_PASS, filter=settings.ROUTES_FILTER, route_name=None, xml=None):
17 self.username = username
18 self.password = password
22 self.filter = settings.ROUTE_FILTER%route_name
25 with manager.connect(host=self.device, port=830, username=self.username, password=self.password) as m:
26 xmlconfig = m.get_config(source='running', filter=('subtree',self.filter)).data_xml
29 def proccess_xml(self):
33 xmlconfig = self.fetch_xml()
35 parser.confile = xmlconfig
36 device = parser.export()
39 def fetch_device(self):
40 device = cache.get("device")
44 device = self.proccess_xml()
45 if device.routing_options:
46 cache.set("device", device, 600)
51 class Applier(object):
52 def __init__(self, route_objects = [], route_object=None, device=settings.NETCONF_DEVICE, username=settings.NETCONF_USER, password=settings.NETCONF_PASS):
53 self.route_object = route_object
54 self.route_objects = route_objects
56 self.username = username
57 self.password = password
61 logger.info("Generating XML config")
62 route_obj = self.route_object
66 flow.routes.append(route)
67 device.routing_options.append(flow)
68 route.name = route_obj.name
69 match = route_obj.match
71 route.match['source'].append(match.matchSource.address)
72 if match.matchDestination:
73 route.match['destination'].append(match.matchDestination.address)
74 if match.matchprotocol:
75 route.match['protocol'].append(match.matchprotocol.protocol)
77 for port in match.matchport.all():
78 route.match['port'].append(port.port)
79 if match.matchDestinationPort:
80 for port in match.matchDestinationPort.all():
81 route.match['destination-port'].append(port.port)
82 if match.matchSourcePort:
83 for port in match.matchSourcePort.all():
84 route.match['source-port'].append(port.port)
85 if match.matchicmpcode:
86 route.match['icmp-code'].append(match.matchicmpcode.icmp_code)
87 if match.matchicmptype:
88 route.match['icmp-type'].append(match.matchicmptype.icmp_type)
89 if match.matchTcpFlag:
90 route.match['tcp-flags'].append(match.matchTcpFlag.tcp_flags)
92 for dscp in match.matchdscp.all():
93 route.match['dscp'].append(dscp.dscp)
94 if match.matchfragmenttype:
95 route.match['fragment'].append(match.matchfragmenttype.fragmenttype)
97 for thenaction in then.thenaction.all():
98 if thenaction.action_value:
99 route.then[thenaction.action] = thenaction.action_value
101 route.then[thenaction.action] = True
102 device = device.export(netconf_config=True)
103 return ET.tostring(device)
107 def delete_route(self):
108 if self.route_object:
109 logger.info("Generating XML config")
110 route_obj = self.route_object
114 flow.routes.append(route)
115 device.routing_options.append(flow)
116 route.name = route_obj.name
117 route.operation = 'delete'
118 device = device.export(netconf_config=True)
119 return ET.tostring(device)
123 def delete_routes(self):
124 if self.route_objects:
125 logger.info("Generating XML config")
128 for route_object in self.route_objects:
129 route_obj = route_object
131 flow.routes.append(route)
132 route.name = route_obj.name
133 route.operation = 'delete'
134 device.routing_options.append(flow)
135 device = device.export(netconf_config=True)
136 return ET.tostring(device)
140 def apply(self, configuration = None):
142 if not configuration:
143 configuration = self.to_xml()
144 edit_is_successful = False
145 commit_confirmed_is_successful = False
146 commit_is_successful = False
148 with manager.connect(host=self.device, port=830, username=self.username, password=self.password) as m:
149 assert(":candidate" in m.server_capabilities)
150 with m.locked(target='candidate'):
153 edit_response = m.edit_config(target='candidate', config=configuration, test_option='test-then-set')
154 edit_is_successful, reason = is_successful(edit_response)
155 logger.info("Successfully edited @ %s" % self.device)
156 if not edit_is_successful:
158 except Exception as e:
159 cause="Caught edit exception: %s %s" %(e,reason)
160 cause=cause.replace('\n', '')
164 if edit_is_successful:
166 commit_confirmed_response = m.commit(confirmed=True, timeout=settings.COMMIT_CONFIRMED_TIMEOUT)
167 commit_confirmed_is_successful, reason = is_successful(commit_confirmed_response)
169 if not commit_confirmed_is_successful:
172 logger.info("Successfully confirmed committed @ %s" % self.device)
173 if not settings.COMMIT:
174 return True, "Successfully confirmed committed"
175 except Exception as e:
176 cause="Caught commit confirmed exception: %s %s" %(e,reason)
177 cause=cause.replace('\n', '')
181 if edit_is_successful and commit_confirmed_is_successful:
183 commit_response = m.commit(confirmed=False)
184 commit_is_successful, reason = is_successful(commit_response)
185 logger.info("Successfully committed @ %s" % self.device)
186 newconfig = m.get_config(source='running', filter=('subtree',settings.ROUTES_FILTER)).data_xml
187 retrieve = Retriever(xml=newconfig)
188 cache.set("device", retrieve.proccess_xml(), 600)
190 if not commit_is_successful:
193 logger.info("Successfully cached device configuration")
194 return True, "Successfully committed"
195 except Exception as e:
196 cause="Caught commit exception: %s %s" %(e,reason)
197 cause=cause.replace('\n', '')
201 return False, "No configuration was supplied"
203 def is_successful(response):
204 from StringIO import StringIO
205 doc = parsexml_(StringIO(response))
206 rootNode = doc.getroot()
207 success_list = rootNode.xpath("//*[local-name()='ok']")
208 if len(success_list)>0:
212 reason_list = rootNode.xpath("//*[local-name()='error-message']")
213 for reason in reason_list:
214 reason_return = "%s %s" %(reason_return, reason.text)
215 return False, reason_return
218 def parsexml_(*args, **kwargs):
219 if 'parser' not in kwargs:
220 kwargs['parser'] = ET.ETCompatXMLParser()
221 doc = ET.parse(*args, **kwargs)