Added missing dirs
[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):
60         if self.route_object:
61             logger.info("Generating XML config")
62             route_obj = self.route_object
63             device = np.Device()
64             flow = np.Flow()
65             route = np.Route()
66             flow.routes.append(route)
67             device.routing_options.append(flow)
68             route.name = route_obj.name
69             match = route_obj.match
70             if match.matchSource:
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)
76             if match.matchport:
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)
91             if match.matchdscp:
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)
96             then = route_obj.then
97             for thenaction in then.thenaction.all():
98                 if thenaction.action_value:
99                     route.then[thenaction.action] = thenaction.action_value
100                 else:
101                     route.then[thenaction.action] = True
102             device = device.export(netconf_config=True)
103             return ET.tostring(device)
104         else:
105             return False
106     
107     def delete_route(self):
108         if self.route_object:
109             logger.info("Generating XML config")
110             route_obj = self.route_object
111             device = np.Device()
112             flow = np.Flow()
113             route = np.Route()
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)
120         else:
121             return False
122
123     def delete_routes(self):
124         if self.route_objects:
125             logger.info("Generating XML config")
126             device = np.Device()
127             flow = np.Flow()
128             for route_object in self.route_objects:
129                 route_obj = route_object
130                 route = np.Route()
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)
137         else:
138             return False    
139     
140     def apply(self, configuration = None):
141         reason = 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
147         if configuration:
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'):
151                     m.discard_changes()
152                     try:
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:
157                             raise Exception()
158                     except Exception as e:
159                         cause="Caught edit exception: %s %s" %(e,reason)
160                         cause=cause.replace('\n', '')
161                         logger.error(cause)
162                         m.discard_changes()
163                         return False, cause
164                     if edit_is_successful:
165                         try:
166                             commit_confirmed_response = m.commit(confirmed=True, timeout=settings.COMMIT_CONFIRMED_TIMEOUT)
167                             commit_confirmed_is_successful, reason = is_successful(commit_confirmed_response)
168                                 
169                             if not commit_confirmed_is_successful:
170                                 raise Exception()
171                             else:
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', '')
178                             logger.error(cause)
179                             return False, cause
180                         if settings.COMMIT:
181                             if edit_is_successful and commit_confirmed_is_successful:
182                                 try:
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)
189                                     
190                                     if not commit_is_successful:
191                                         raise Exception()
192                                     else:
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', '')
198                                     logger.error(cause)
199                                     return False, cause
200         else:
201             return False, "No configuration was supplied"
202             
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:
209         return True, None
210     else:
211         reason_return = ''
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
216     
217     
218 def parsexml_(*args, **kwargs):
219     if 'parser' not in kwargs:
220         kwargs['parser'] = ET.ETCompatXMLParser()
221     doc = ET.parse(*args, **kwargs)
222     return doc
223