4 # Copyright 2016-2017 RIFT.IO Inc
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
30 gi
.require_version('RwYang', '1.0')
31 gi
.require_version('RwProjectVnfdYang', '1.0')
32 gi
.require_version('RwProjectNsdYang', '1.0')
33 gi
.require_version('RwProjectYang', '1.0')
34 from gi
.repository
import (
36 RwProjectVnfdYang
as RwVnfdYang
,
37 RwProjectNsdYang
as RwNsdYang
,
41 logging
.basicConfig(level
=logging
.WARNING
)
42 logger
= logging
.getLogger("openmano2rift.py")
45 class UnknownVNFError(Exception):
49 class DescriptorFileWriter(object):
50 def __init__(self
, module_list
, output_dir
, output_format
):
51 self
._model
= RwYang
.Model
.create_libncx()
52 for module
in module_list
:
53 self
._model
.load_module(module
)
55 self
._output
_dir
= output_dir
56 self
._output
_format
= output_format
58 def _write_file(self
, file_name
, output
):
59 file_path
= os
.path
.join(self
._output
_dir
, file_name
)
60 dir_path
= os
.path
.dirname(file_path
)
61 if not os
.path
.exists(dir_path
):
64 with
open(file_path
, "w") as hdl
:
67 logger
.info("Wrote descriptor to %s", file_path
)
69 def _write_json(self
, descriptor
, subdir
):
71 '%s.json' % os
.path
.join(descriptor
.name
, subdir
, descriptor
.name
),
72 descriptor
.descriptor
.to_json(self
._model
)
75 def _write_xml(self
, descriptor
, subdir
):
77 '%s.xml' % os
.path
.join(descriptor
.name
, subdir
, descriptor
.name
),
78 descriptor
.descriptor
.to_xml_v2(self
._model
, pretty_print
=True)
81 def _write_yaml(self
, descriptor
, subdir
):
83 '%s.yaml' % os
.path
.join(descriptor
.name
, subdir
, descriptor
.name
),
84 yaml
.dump(descriptor
.descriptor
.as_dict()),
87 def write_descriptor(self
, descriptor
, subdir
=""):
88 if self
._output
_format
== 'json':
89 self
._write
_json
(descriptor
, subdir
=subdir
)
91 elif self
._output
_format
== 'xml':
92 self
._write
_xml
(descriptor
, subdir
=subdir
)
94 elif self
._output
_format
== 'yaml':
95 self
._write
_yaml
(descriptor
, subdir
=subdir
)
98 class RiftManoDescriptor(object):
99 def __init__(self
, openmano
=None):
100 self
.openmano
= openmano
101 self
.descriptor
= None
104 class RiftNS(RiftManoDescriptor
):
105 def __init__(self
, openmano
=None):
106 super().__init
__(openmano
)
107 self
.nsd_catalog
= None
111 def get_vnfd_id(self
, vnf_list
, vnf_name
):
113 if vnf
.name
== vnf_name
:
116 # Didn't find the vnf just return the vnf_name
119 def openmano2rift(self
, vnf_list
):
120 self
.descriptor
= RwNsdYang
.YangData_RwProject_Project_NsdCatalog()
121 openmano_nsd
= self
.openmano
.dictionary
122 self
.name
= openmano_nsd
['name']
123 nsd
= self
.descriptor
.nsd
.add()
124 nsd
.id = str(uuid
.uuid1())
126 nsd
.short_name
= self
.name
127 nsd
.description
= openmano_nsd
['description']
129 nodes
= openmano_nsd
['topology']['nodes']
130 connections
= openmano_nsd
['topology']['connections']
132 def create_consituent_vnfds():
133 vnf_member_index_dict
= {}
135 vnfd_idx_gen
= itertools
.count(1)
138 if node
['type'] != 'VNF':
141 vnfd_idx
= next(vnfd_idx_gen
)
142 constituent_vnfd
= nsd
.constituent_vnfd
.add()
143 constituent_vnfd
.member_vnf_index
= vnfd_idx
144 constituent_vnfd
.vnfd_id_ref
= self
.get_vnfd_id(vnf_list
, node
['VNF model'])
145 vnf_member_index_dict
[key
] = vnfd_idx
147 return vnf_member_index_dict
149 def create_connections(vnf_member_index_dict
):
150 keys
= connections
.keys()
152 # TODO: Need clarification from TEF
153 # skip the mgmtnet in OpenMANO descriptor
156 conn
= connections
[key
]
159 id=str(uuid
.uuid1()),
165 nodes
= conn
['nodes']
166 for node
, node_keys
in [(node
, node
.keys()) for node
in nodes
]:
167 for node_key
in node_keys
:
168 topo_node
= openmano_nsd
['topology']['nodes'][node_key
]
169 if topo_node
['type'] == 'VNF':
170 cpref
= vld
.vnfd_connection_point_ref
.add()
171 cpref
.from_dict(dict(
172 member_vnf_index_ref
=vnf_member_index_dict
[node_key
],
173 vnfd_id_ref
=self
.get_vnfd_id(vnf_list
, topo_node
['VNF model']),
174 #vnfd_id_ref=topo_node['VNF model'],
175 vnfd_connection_point_ref
=node
[node_key
],
177 if key
!= 'control-net':
178 vld
.provider_network
.physical_network
= 'physnet_sriov'
179 vld
.provider_network
.overlay_type
= 'VLAN'
181 vnf_member_index_dict
= create_consituent_vnfds()
182 create_connections(vnf_member_index_dict
)
185 class RiftVnfd(RiftManoDescriptor
):
186 def __init__(self
, openmano
=None):
187 super().__init
__(openmano
)
188 self
.vnfd_catalog
= None
191 def find_external_connection(self
, vdu_name
, if_name
):
193 Find if the vdu interface has an external connection.
195 openmano_vnfd
= self
.openmano
.dictionary
['vnf']
196 if 'external-connections' not in openmano_vnfd
:
199 ext_conn_list
= openmano_vnfd
['external-connections']
200 for ext_conn
in ext_conn_list
:
201 if ((ext_conn
['VNFC'] == vdu_name
) and
202 (ext_conn
['local_iface_name'] == if_name
)):
207 def openmano2rift(self
):
208 self
.descriptor
= RwVnfdYang
.YangData_RwProject_Project_VnfdCatalog()
209 vnfd
= self
.descriptor
.vnfd
.add()
211 vnfd
.id = str(uuid
.uuid1())
213 openmano_vnfd
= self
.openmano
.dictionary
['vnf']
214 self
.name
= openmano_vnfd
['name']
215 vnfd
.name
= self
.name
216 if "description" in openmano_vnfd
:
217 vnfd
.description
= openmano_vnfd
['description']
219 # Parse and add all the external connection points
220 if 'external-connections' in openmano_vnfd
:
221 ext_conn_list
= openmano_vnfd
['external-connections']
223 for ext_conn
in ext_conn_list
:
225 if ext_conn
['name'] == 'eth0':
227 conn_point
= vnfd
.connection_point
.add()
228 conn_point
.name
= ext_conn
['name']
229 conn_point
.type_yang
= 'VPORT'
231 # TODO: Need a concrete example of how openmano descriptor
232 # uses internal connections.
233 if 'internal-connections' in openmano_vnfd
:
234 int_conn_list
= openmano_vnfd
['internal-connections']
236 def add_external_interfaces(vdu
, numa
):
237 if 'interfaces' not in numa
:
240 numa_if_list
= numa
['interfaces']
241 for numa_if
in numa_if_list
:
242 ext_conn
= self
.find_external_connection(vdu
.name
, numa_if
['name'])
246 ext_iface
= vdu
.external_interface
.add()
247 ext_iface
.name
= numa_if
['name']
248 ext_iface
.vnfd_connection_point_ref
= ext_conn
['name']
249 ext_iface
.virtual_interface
.vpci
= numa_if
['vpci']
250 if numa_if
['dedicated'] == 'no':
251 ext_iface
.virtual_interface
.type_yang
= 'SR_IOV'
253 ext_iface
.virtual_interface
.type_yang
= 'PCI_PASSTHROUGH'
255 vnfc_list
= openmano_vnfd
['VNFC']
256 for vnfc
in vnfc_list
:
259 id=str(uuid
.uuid1()),
261 image
=vnfc
['VNFC image'],
262 vm_flavor
={"storage_gb": vnfc
["disk"] if "disk" in vnfc
else 20},
264 if "description" in vnfc
:
265 vdu_dict
["description"] = vnfc
['description']
267 vdu
.from_dict(vdu_dict
)
269 vnfd
.mgmt_interface
.vdu_id
= vdu
.id
271 numa_list
= vnfc
['numas']
276 for numa
in numa_list
:
277 node
= vdu
.guest_epa
.numa_node_policy
.node
.add()
278 node
.id = numa_node_cnt
279 # node.memory_mb = int(numa['memory']) * 1024
282 memory
= memory
+ node
.memory_mb
283 # Need a better explanation of "cores", "paired-threads", "threads"
284 # in openmano descriptor. Particularly how they map to cpu and
285 # thread pinning policies
286 if 'paired-threads' in numa
:
287 vcpu_count
= vcpu_count
+ int(numa
['paired-threads']) * 2
290 vcpu_count
= vcpu_count
+ int(numa
['cores'])
292 add_external_interfaces(vdu
, numa
)
295 # vdu.vm_flavor.memory_mb = memory
296 vdu
.vm_flavor
.memory_mb
= 12 * 1024
297 vdu
.vm_flavor
.vcpu_count
= vcpu_count
298 vdu
.guest_epa
.numa_node_policy
.node_cnt
= numa_node_cnt
299 vdu
.guest_epa
.numa_node_policy
.mem_policy
= 'STRICT'
300 vdu
.guest_epa
.mempage_size
= 'LARGE'
301 vdu
.guest_epa
.cpu_pinning_policy
= 'DEDICATED'
302 vdu
.guest_epa
.cpu_thread_pinning_policy
= 'PREFER'
304 # TODO: Enable hypervisor epa
305 # vdu.hypervisor_epa.version = vnfc['hypervisor']['version']
306 # if vnfc['hypervisor']['type'] == 'QEMU-kvm':
307 # vdu.hypervisor_epa.type_yang = 'REQUIRE_KVM'
309 # vdu.hypervisor_epa.type_yang = 'PREFER_KVM'
311 # TODO: Enable host epa
312 # vdu.host_epa.cpu_feature = vnfc['processor']['features']
314 # Parse the bridge interfaces
315 if 'bridge-ifaces' in vnfc
:
316 bridge_ifaces
= vnfc
['bridge-ifaces']
319 for bridge_iface
in bridge_ifaces
:
321 if bridge_iface
['name'] == 'eth0':
324 ext_conn
= self
.find_external_connection(vdu
.name
,
325 bridge_iface
['name'])
327 ext_iface
= vdu
.external_interface
.add()
328 ext_iface
.name
= bridge_iface
['name']
329 ext_iface
.vnfd_connection_point_ref
= ext_conn
['name']
330 if 'vpci' in bridge_iface
:
331 ext_iface
.virtual_interface
.vpci
= bridge_iface
['vpci']
332 ext_iface
.virtual_interface
.type_yang
= 'VIRTIO'
334 # set vpci information for the 'default' network
335 # TODO: This needs to be inferred gtom bridge ifaces,
336 # need input from TEF
337 vdu
.mgmt_vpci
= "0000:00:0a.0"
340 class OpenManoDescriptor(object):
341 def __init__(self
, yaml_file_hdl
):
342 self
.dictionary
= yaml
.load(yaml_file_hdl
)
346 """ The descriptor type (ns or vnf)"""
347 if 'vnf' in self
.dictionary
:
353 """ Dump the Descriptor out to stdout """
354 print(yaml
.dump(self
.dictionary
))
357 def is_writable_directory(dir_path
):
358 """ Returns True if dir_path is writable, False otherwise
361 dir_path - A directory path
363 if not os
.path
.exists(dir_path
):
364 raise ValueError("Directory does not exist: %s", dir_path
)
367 testfile
= tempfile
.TemporaryFile(dir=dir_path
)
375 def create_vnfs_from_yaml_files(yaml_file_hdls
):
376 """ Create a list of RiftVnfd instances from yaml file handles
379 yaml_file_hdls - OpenMano Yaml file handles
382 A list of RiftVnfd instances
385 for yaml_file_hdl
in yaml_file_hdls
:
386 openmano
= OpenManoDescriptor(yaml_file_hdl
)
387 yaml_file_hdl
.seek(0)
389 if openmano
.type != "vnf":
392 vnf
= RiftVnfd(openmano
)
399 def create_ns_from_yaml_files(yaml_file_hdls
, vnf_list
):
400 """ Create a list of RiftNS instances from yaml file handles
403 yaml_file_hdls - OpenMano Yaml file handles
404 vnf_list - list of RiftVnfd
407 A list of RiftNS instances
410 for yaml_file_hdl
in yaml_file_hdls
:
411 openmano
= OpenManoDescriptor(yaml_file_hdl
)
412 if openmano
.type != "ns":
415 net_svc
= RiftNS(openmano
)
416 net_svc
.openmano2rift(vnf_list
)
417 ns_list
.append(net_svc
)
422 def parse_args(argv
=sys
.argv
[1:]):
423 """ Parse the command line arguments
426 arv - The list of arguments to parse
429 Argparse Namespace instance
432 parser
= argparse
.ArgumentParser()
436 help="Directory to output converted descriptors",
441 choices
=['yaml', 'xml', 'json'],
443 help="Descriptor output format",
450 type=argparse
.FileType('r'),
451 help="OpenMano YAML Descriptor File",
454 args
= parser
.parse_args(argv
)
456 if not os
.path
.exists(args
.outdir
):
457 os
.makedirs(args
.outdir
)
459 if not is_writable_directory(args
.outdir
):
460 logging
.error("Directory %s is not writable", args
.outdir
)
466 def main(argv
=sys
.argv
[1:]):
467 args
= parse_args(argv
)
469 vnf_list
= create_vnfs_from_yaml_files(args
.yaml_file_hdls
)
470 ns_list
= create_ns_from_yaml_files(args
.yaml_file_hdls
, vnf_list
)
472 # TODO (Philip): Relook at the model generation
473 writer
= DescriptorFileWriter(
474 module_list
=['rw-project', 'project-nsd', 'rw-project-nsd', 'project-vnfd', 'rw-project-vnfd'],
475 output_dir
=args
.outdir
,
476 output_format
=args
.format
,
479 for nw_svc
in ns_list
:
480 writer
.write_descriptor(nw_svc
, subdir
="nsd")
483 writer
.write_descriptor(vnf
, subdir
="vnfd")
486 if __name__
== "__main__":