Khen Nursimulu | a7b842a | 2016-12-03 23:28:42 -0500 | [diff] [blame] | 1 | #!/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 Nursimulu | a497274 | 2016-12-23 17:15:20 -0500 | [diff] [blame] | 19 | import structlog |
| 20 | from lxml import etree |
| 21 | import netconf.nc_common.error as ncerror |
| 22 | |
| 23 | log = structlog.get_logger() |
| 24 | |
Khen Nursimulu | a7b842a | 2016-12-03 23:28:42 -0500 | [diff] [blame] | 25 | |
| 26 | class 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 Nursimulu | a497274 | 2016-12-23 17:15:20 -0500 | [diff] [blame] | 32 | 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 |