47b9725b7e066a5767c3934394c307e793ae23fa
[ncclient] / ncclient / rpc / reply.py
1 # Copyright 2009 Shikhar Bhushan
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 from xml.etree import cElementTree as ET
16
17 from ncclient.content import multiqualify as _
18 from ncclient.content import unqualify as __
19
20 import logging
21 logger = logging.getLogger('ncclient.rpc.reply')
22
23 class RPCReply:
24     
25     def __init__(self, raw):
26         self._raw = raw
27         self._parsed = False
28         self._root = None
29         self._errors = []
30     
31     def __repr__(self):
32         return self._raw
33     
34     def parse(self):
35         if self._parsed: return
36         root = self._root = ET.fromstring(self._raw) # <rpc-reply> element
37         
38         if __(root.tag) != 'rpc-reply':
39             raise ValueError('Root element is not RPC reply')
40         
41         ok = False
42         # per rfc 4741 an <ok/> tag is sent when there are no errors or warnings
43         oktags = _('ok')
44         for oktag in oktags:
45             if root.find(oktag) is not None:
46                 logger.debug('parsed [%s]' % oktag)
47                 break
48         else:
49             # create RPCError objects from <rpc-error> elements
50             errtags = _('rpc-error')
51             for errtag in errtags:
52                 for err in root.getiterator(errtag): # a particular <rpc-error>
53                     logger.debug('parsed [%s]' % errtag)
54                     d = {}
55                     for err_detail in err.getchildren(): # <error-type> etc..
56                         tag = __(err_detail.tag)
57                         d[tag] = (err_detail.text.strip() if tag != 'error-info'
58                                   else ET.tostring(err_detail, 'utf-8'))
59                     self._errors.append(RPCError(d))
60                 if self._errors:
61                     break
62         
63         if self.ok:
64             # TODO: store children in some way...
65             pass
66         
67         self._parsed = True
68     
69     @property
70     def raw(self):
71         return self._raw
72     
73     @property
74     def root(self):
75         return self._root
76     
77     @property
78     def ok(self):
79         if not self._parsed: self.parse()
80         return not self._errors # empty list => false
81     
82     @property
83     def errors(self):
84         'List of RPCError objects. Will be empty if no <rpc-error> elements in reply.'
85         if not self._parsed: self.parse()
86         return self._errors
87
88
89 class RPCError(Exception): # raise it if you like
90     
91     def __init__(self, err_dict):
92         self._dict = err_dict
93         if self.message is not None:
94             Exception.__init__(self, self.message)
95         else:
96             Exception.__init__(self)
97     
98     @property
99     def raw(self):
100         return self._element.tostring()
101     
102     @property
103     def type(self):
104         return self.get('error-type', None)
105     
106     @property
107     def severity(self):
108         return self.get('error-severity', None)
109     
110     @property
111     def tag(self):
112         return self.get('error-tag', None)
113     
114     @property
115     def path(self):
116         return self.get('error-path', None)
117     
118     @property
119     def message(self):
120         return self.get('error-message', None)
121     
122     @property
123     def info(self):
124         return self.get('error-info', None)
125
126     ## dictionary interface
127     
128     __getitem__ = lambda self, key: self._dict.__getitem__(key)
129     
130     __iter__ = lambda self: self._dict.__iter__()
131     
132     __contains__ = lambda self, key: self._dict.__contains__(key)
133     
134     keys = lambda self: self._dict.keys()
135     
136     get = lambda self, key, default: self._dict.get(key, default)
137         
138     iteritems = lambda self: self._dict.iteritems()
139     
140     iterkeys = lambda self: self._dict.iterkeys()
141     
142     itervalues = lambda self: self._dict.itervalues()
143     
144     values = lambda self: self._dict.values()
145     
146     items = lambda self: self._dict.items()
147     
148     __repr__ = lambda self: repr(self._dict)