Slight change in datetime import. Addition of has expired func in Route
[flowspy] / utils / proxy.py
1 import nxpy as np
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
6 import logging
7 from django.core.cache import cache
8
9 FORMAT = '%(asctime)s %(levelname)s: %(message)s'
10 logging.basicConfig(format=FORMAT)
11 logger = logging.getLogger(__name__)
12 logger.setLevel(logging.DEBUG)
13
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):
16         self.device = device
17         self.username = username
18         self.password = password
19         self.filter = filter
20         self.xml = xml
21         if route_name:
22             self.filter = settings.ROUTE_FILTER%route_name
23     
24     def fetch_xml(self):
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
27         return xmlconfig
28     
29     def proccess_xml(self):
30         if self.xml:
31             xmlconfig = self.xml
32         else:
33             xmlconfig = self.fetch_xml()
34         parser = np.Parser()
35         parser.confile = xmlconfig
36         device = parser.export()
37         return device    
38     
39     def fetch_device(self):
40         device = cache.get("device")
41         if device:
42             return device
43         else:
44             device = self.proccess_xml()
45             if device.routing_options:
46                 cache.set("device", device, 600)
47                 return device
48             else:
49                 return False
50
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
55         self.device = device
56         self.username = username
57         self.password = password
58     
59     def to_xml(self, operation=None):
60         logger.info("Operation: %s"%operation)
61         if self.route_object:
62             logger.info("Generating XML config")
63             route_obj = self.route_object
64             device = np.Device()
65             flow = np.Flow()
66             route = np.Route()
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)
75             if route_obj.source:
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)
81             try:
82                 if route_obj.port:
83                     for port in route_obj.port.all():
84                         route.match['port'].append(port.port)
85             except:
86                 pass
87             try:
88                 if route_obj.destinationport:
89                     for port in route_obj.destinationport.all():
90                         route.match['destination-port'].append(port.port)
91             except:
92                 pass
93             try:
94                 if route_obj.sourceport:
95                     for port in route_obj.sourceport.all():
96                         route.match['source-port'].append(port.port)
97             except:
98                 pass
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)
105             try:
106                 if route_obj.dscp:
107                     for dscp in route_obj.dscp.all():
108                         route.match['dscp'].append(dscp.dscp)
109             except:
110                 pass
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
116                 else:
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)
123         else:
124             return False
125
126     def delete_routes(self):
127         if self.route_objects:
128             logger.info("Generating XML config")
129             device = np.Device()
130             flow = np.Flow()
131             for route_object in self.route_objects:
132                 route_obj = route_object
133                 route = np.Route()
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)
140         else:
141             return False    
142     
143     def apply(self, configuration = None, operation=None):
144         reason = 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
150         if configuration:
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'):
154                     m.discard_changes()
155                     try:
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:
160                             raise Exception()
161                     except Exception as e:
162                         cause="Caught edit exception: %s %s" %(e,reason)
163                         cause=cause.replace('\n', '')
164                         logger.error(cause)
165                         m.discard_changes()
166                         return False, cause
167                     if edit_is_successful:
168                         try:
169                             commit_confirmed_response = m.commit(confirmed=True, timeout=settings.COMMIT_CONFIRMED_TIMEOUT)
170                             commit_confirmed_is_successful, reason = is_successful(commit_confirmed_response)
171                                 
172                             if not commit_confirmed_is_successful:
173                                 raise Exception()
174                             else:
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', '')
181                             logger.error(cause)
182                             return False, cause
183                         if settings.COMMIT:
184                             if edit_is_successful and commit_confirmed_is_successful:
185                                 try:
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)
192                                     
193                                     if not commit_is_successful:
194                                         raise Exception()
195                                     else:
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', '')
201                                     logger.error(cause)
202                                     return False, cause
203         else:
204             return False, "No configuration was supplied"
205             
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:
212         return True, None
213     else:
214         reason_return = ''
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
219     
220     
221 def parsexml_(*args, **kwargs):
222     if 'parser' not in kwargs:
223         kwargs['parser'] = ET.ETCompatXMLParser()
224     doc = ET.parse(*args, **kwargs)
225     return doc
226