blob: 584e35b3e562150171862e52f39a044a5035629f [file] [log] [blame]
Khen Nursimulua7b842a2016-12-03 23:28:42 -05001#!/usr/bin/env python
2#
3# Copyright 2016 the original author or authors.
4#
5# Code adapted from https://github.com/choppsv1/netconf
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
Khen Nursimulua4972742016-12-23 17:15:20 -050019import structlog
20from lxml import etree
21import netconf.nc_common.error as ncerror
22
23log = structlog.get_logger()
24
Khen Nursimulua7b842a2016-12-03 23:28:42 -050025
26class RpcResponse():
27 def __init__(self):
28 self.is_error = False
29 # if there is an error then the reply_node will contains an Error
30 # object
31 self.reply_node = None
Khen Nursimulua4972742016-12-23 17:15:20 -050032 self.close_session = False
33
34 def build_xml_response(self, request, voltha_response):
35 if request is None:
36 return
37 voltha_xml_string = etree.tostring(voltha_response)
38
39 # Remove the leading and trailing <root> tags
40 if voltha_xml_string.startswith('<yang>'):
41 voltha_xml_string = voltha_xml_string[len('<yang>'):]
42 if voltha_xml_string.endswith('</yang>'):
43 voltha_xml_string = voltha_xml_string[:-len('</yang>')]
44
45 # Create the xml body as
46 if request.has_key('subclass'):
47 body = ''.join([
48 '<data>',
49 '<',
50 request['class'],
51 ' xmlns="urn:opencord:params:xml:ns:voltha:ietf-voltha">',
52 '<',
53 request['subclass'],
54 '>',
55 voltha_xml_string,
56 '</',
57 request['subclass'],
58 '>',
59 '</',
60 request['class'],
61 '>',
62 '</data>'
63 ])
64 else:
65 body = ''.join([
66 '<data>',
67 '<',
68 request['class'],
69 ' xmlns="urn:opencord:params:xml:ns:voltha:ietf-voltha">',
70 voltha_xml_string,
71 '</',
72 request['class'],
73 '>',
74 '</data>'
75 ])
76
77 return etree.fromstring(body)
78
79 def add_node(self, new_node, tree):
80 if new_node.tag == 'ignore':
81 # We want only sub-elements
82 for elem in list(new_node):
83 tree.append(elem)
84 else:
85 tree.append(new_node)
86
87 def copy_basic_element(self, elm):
88 new_elem = etree.Element(elm.tag)
89 new_elem.text = elm.text
90 return new_elem
91
92 def process_inline_option(self, elem):
93 inline_option = False
94 inline_node_name = None
95 for elm in list(elem):
96 if elm.tag == 'yang_field_option':
97 inline_option = True
98 if elm.tag == 'name':
99 inline_node_name = elm.text
100 if not inline_option:
101 new_elem = etree.Element(elem.tag)
102 return new_elem, elem
103
104 # look for the node with the inline_node_name
105 for elm in list(elem):
106 if elm.tag == inline_node_name:
107 new_elem = etree.Element('ignore')
108 return new_elem, elm
109
110 def process_element(self, elem):
111 attrib = elem.get('type')
112 if (attrib == 'list'):
113 if list(elem) is None:
114 return self.copy_basic_element(elem)
115 new_elem = etree.Element('ignore')
116 for elm in list(elem):
117 elm.tag = elem.tag
118 if elm.get('type') in ['list', 'dict']:
119 self.add_node(self.process_element(elm), new_elem)
120 else:
121 new_elem.append(self.copy_basic_element(elm))
122 return new_elem
123 elif (attrib == 'dict'):
124 # Empty case
125 if list(elem) is None:
126 return self.copy_basic_element(elem)
127
128 # Process field option.
129 new_elem, elem = self.process_inline_option(elem)
130
131 for elm in list(elem):
132 if elm.get('type') in ['list', 'dict']:
133 self.add_node(self.process_element(elm), new_elem)
134 else:
135 new_elem.append(self.copy_basic_element(elm))
136 return new_elem
137 else:
138 return self.copy_basic_element(elem)
139
140 def to_yang_xml(self, from_xml):
141 # Parse from_xml as follows:
142 # 1. Any element having a list attribute shoud have each item move 1 level
143 # up and retag using the parent tag
144 # 2. Any element having a dict attribute and has a <yang_field_option>
145 # sub-element should have all it's items move to teh parent level
146 top = etree.Element('yang')
147 elms = list(from_xml)
148
149 # special case the xml contain a list type
150 if len(elms) == 1:
151 item = elms[0]
152 if item.get('type') == 'list':
153 item.tag = 'ignore'
154 self.add_node(self.process_element(item), top)
155 return top
156
157 # Process normally for all other cases
158 for elm in elms:
159 self.add_node(self.process_element(elm), top)
160
161 return top
162
163 def build_yang_response(self, root, request):
164 try:
165 yang_xml = self.to_yang_xml(root)
166 log.info('yang-xml', yang_xml=etree.tounicode(yang_xml,
167 pretty_print=True))
168 return self.build_xml_response(request, yang_xml)
169 except Exception as e:
170 self.rpc_response.is_error = True
171 self.rpc_response.node = ncerror.BadMsg(request)
172 return