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
59 def to_xml(self, operation=None):
60 logger.info("Operation: %s"%operation)
62 logger.info("Generating XML config")
63 route_obj = self.route_object
67 flow.routes.append(route)
68 device.routing_options.append(flow)
69 route.name = route_obj.name
70 if operation == "delete":
71 logger.info("Requesting a delete operation")
72 route.operation = operation
73 device = device.export(netconf_config=True)
74 return ET.tostring(device)
76 route.match['source'].append(route_obj.source)
77 if route_obj.destination:
78 route.match['destination'].append(route_obj.destination)
79 if route_obj.protocol:
80 route.match['protocol'].append(route_obj.protocol)
83 for port in route_obj.port.all():
84 route.match['port'].append(port.port)
88 if route_obj.destinationport:
89 for port in route_obj.destinationport.all():
90 route.match['destination-port'].append(port.port)
94 if route_obj.sourceport:
95 for port in route_obj.sourceport.all():
96 route.match['source-port'].append(port.port)
99 if route_obj.icmpcode:
100 route.match['icmp-code'].append(route_obj.icmpcode)
101 if route_obj.icmptype:
102 route.match['icmp-type'].append(route_obj.icmptype)
103 if route_obj.tcpflag:
104 route.match['tcp-flags'].append(route_obj.tcpflag)
107 for dscp in route_obj.dscp.all():
108 route.match['dscp'].append(dscp.dscp)
111 if route_obj.fragmenttype:
112 route.match['fragment'].append(route_obj.fragmenttype)
113 for thenaction in route_obj.then.all():
114 if thenaction.action_value:
115 route.then[thenaction.action] = thenaction.action_value
117 route.then[thenaction.action] = True
118 if operation == "replace":
119 logger.info("Requesting a replace operation")
120 route.operation = operation
121 device = device.export(netconf_config=True)
122 return ET.tostring(device)
126 def delete_routes(self):
127 if self.route_objects:
128 logger.info("Generating XML config")
131 for route_object in self.route_objects:
132 route_obj = route_object
134 flow.routes.append(route)
135 route.name = route_obj.name
136 route.operation = 'delete'
137 device.routing_options.append(flow)
138 device = device.export(netconf_config=True)
139 return ET.tostring(device)
143 def apply(self, configuration = None, operation=None):
145 if not configuration:
146 configuration = self.to_xml(operation=operation)
147 edit_is_successful = False
148 commit_confirmed_is_successful = False
149 commit_is_successful = False
151 with manager.connect(host=self.device, port=830, username=self.username, password=self.password) as m:
152 assert(":candidate" in m.server_capabilities)
153 with m.locked(target='candidate'):
156 edit_response = m.edit_config(target='candidate', config=configuration, test_option='test-then-set')
157 edit_is_successful, reason = is_successful(edit_response)
158 logger.info("Successfully edited @ %s" % self.device)
159 if not edit_is_successful:
161 except Exception as e:
162 cause="Caught edit exception: %s %s" %(e,reason)
163 cause=cause.replace('\n', '')
167 if edit_is_successful:
169 commit_confirmed_response = m.commit(confirmed=True, timeout=settings.COMMIT_CONFIRMED_TIMEOUT)
170 commit_confirmed_is_successful, reason = is_successful(commit_confirmed_response)
172 if not commit_confirmed_is_successful:
175 logger.info("Successfully confirmed committed @ %s" % self.device)
176 if not settings.COMMIT:
177 return True, "Successfully confirmed committed"
178 except Exception as e:
179 cause="Caught commit confirmed exception: %s %s" %(e,reason)
180 cause=cause.replace('\n', '')
184 if edit_is_successful and commit_confirmed_is_successful:
186 commit_response = m.commit(confirmed=False)
187 commit_is_successful, reason = is_successful(commit_response)
188 logger.info("Successfully committed @ %s" % self.device)
189 newconfig = m.get_config(source='running', filter=('subtree',settings.ROUTES_FILTER)).data_xml
190 retrieve = Retriever(xml=newconfig)
191 cache.set("device", retrieve.proccess_xml(), 600)
193 if not commit_is_successful:
196 logger.info("Successfully cached device configuration")
197 return True, "Successfully committed"
198 except Exception as e:
199 cause="Caught commit exception: %s %s" %(e,reason)
200 cause=cause.replace('\n', '')
204 return False, "No configuration was supplied"
206 def is_successful(response):
207 from StringIO import StringIO
208 doc = parsexml_(StringIO(response))
209 rootNode = doc.getroot()
210 success_list = rootNode.xpath("//*[local-name()='ok']")
211 if len(success_list)>0:
215 reason_list = rootNode.xpath("//*[local-name()='error-message']")
216 for reason in reason_list:
217 reason_return = "%s %s" %(reason_return, reason.text)
218 return False, reason_return
221 def parsexml_(*args, **kwargs):
222 if 'parser' not in kwargs:
223 kwargs['parser'] = ET.ETCompatXMLParser()
224 doc = ET.parse(*args, **kwargs)