3 # Copyright 2016 RIFT.IO Inc
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
19 from gi
import require_version
20 require_version('RwCal', '1.0')
21 import rift
.rwcal
.openmano_vimconnector
as vimconn_openvim
28 from gi
.repository
import (
35 import rift
.cal
.rwcal_status
as rwcal_status
38 logger
= logging
.getLogger('rwcal.openmano_vimconnector')
40 class UnknownAccountError(Exception):
43 class OpenvimCALOperationFailure(Exception):
46 class MissingFileError(Exception):
50 class ImageLocationError(Exception):
53 class UninitializedPluginError(Exception):
56 rwstatus_exception_map
= {IndexError: RwTypes
.RwStatus
.NOTFOUND
,
57 KeyError: RwTypes
.RwStatus
.NOTFOUND
,
58 UnknownAccountError
: RwTypes
.RwStatus
.NOTFOUND
,
59 MissingFileError
: RwTypes
.RwStatus
.NOTFOUND
,
62 rwstatus
= rw_status
.rwstatus_from_exc_map(rwstatus_exception_map
)
63 rwcalstatus
= rwcal_status
.rwcalstatus_from_exc_map(rwstatus_exception_map
)
66 class RwcalOpenmanoVimConnector(GObject
.Object
, RwCal
.Cloud
):
67 """Stub implementation the CAL VALA methods for Openmano. """
71 GObject
.Object
.__init
__(self
)
72 self
._driver
_class
= vimconn_openvim
.vimconnector
73 self
.log
= logging
.getLogger('rwcal.openmano_vimconnector.%s' % RwcalOpenmanoVimConnector
.instance_num
)
74 self
.log
.setLevel(logging
.DEBUG
)
75 self
._rwlog
_handler
= None
76 self
._tenant
_name
= None
77 RwcalOpenmanoVimConnector
.instance_num
+= 1
79 @contextlib.contextmanager
80 def _use_driver(self
, account
):
81 #if self._rwlog_handler is None:
82 # raise UninitializedPluginError("Must call init() in CAL plugin before use.")
84 #with rwlogger.rwlog_root_handler(self._rwlog_handler):
86 if self
._tenant
_name
!= account
.openvim
.tenant_name
:
87 tmp_drv
= self
._driver
_class
(uuid
= '',
89 #tenant_id = account.openvim.tenant_id,
92 url
='http://{}:{}/openvim'.format(account
.openvim
.host
,account
.openvim
.port
),
94 tenant_dict
= {'name':account
.openvim
.tenant_name
}
95 tenant_list
= tmp_drv
.get_tenant_list(tenant_dict
)
96 if len(tenant_list
) == 0:
97 tmp_drv
.new_tenant(account
.openvim
.tenant_name
,"default tenant")
98 self
._tenant
_name
= account
.openvim
.tenant_name
100 self
._tenant
_name
= account
.openvim
.tenant_name
103 drv
= self
._driver
_class
(uuid
= '',
105 #tenant_id = account.openvim.tenant_id,
107 tenant_name
= account
.openvim
.tenant_name
,
108 url
='http://{}:{}/openvim'.format(account
.openvim
.host
,account
.openvim
.port
),
111 except Exception as e
:
112 self
.log
.error("RwcalOpenmanoVimConnectorPlugin: VimConnector init failed. Exception: %s" %(str(e
)))
118 def do_init(self
, rwlog_ctx
):
119 if not any(isinstance(h
, rwlogger
.RwLogger
) for h
in logger
.handlers
):
122 category
="rw-cal-log",
123 subcategory
="openmano_vimconnector",
128 @rwstatus(ret_on_failure
=[None])
129 def do_validate_cloud_creds(self
, account
):
131 Validates the cloud account credentials for the specified account.
132 If creds are not valid, returns an error code & reason string
134 account - a cloud account to validate
137 Validation Code and Details String
139 status
= RwcalYang
.CloudConnectionStatus()
140 url
= 'http://{}:{}/openvim/'.format(account
.openvim
.host
,account
.openvim
.port
)
142 r
=requests
.get(url
,timeout
=3)
144 except requests
.exceptions
.HTTPError
as e
:
145 self
.log
.error("OpenvimConnectorPlugin: Openvim account credential validation failed. Exception: %s", str(e
))
146 status
.status
= "failure"
147 status
.details
= "Invalid Credentials: %s" % str(e
)
148 except Exception as e
:
149 self
.log
.error("OpenvimConnectorPlugin: Openvim connection failed. Exception: %s", str(e
))
150 status
.status
= "failure"
151 status
.details
= "Connection Failed (Invlaid URL): %s" % str(e
)
153 self
.log
.debug("Openvim Successfully connected")
154 status
.status
= "success"
155 status
.details
= "Connection was successful"
159 @rwstatus(ret_on_failure
=[None])
160 def do_get_management_network(self
, account
):
161 raise NotImplementedError()
164 def do_create_tenant(self
, account
, name
):
165 with self
._use
_driver
(account
) as drv
:
166 return drv
.new_tenant(name
, "New CAL teannt");
169 def do_delete_tenant(self
, account
, tenant_id
):
170 with self
._use
_driver
(account
) as drv
:
171 drv
.delete_tenant(tenant_id
);
174 def _fill_tenant_info(tenant_info
):
175 """Create a GI object from tenant info dictionary
177 Converts tenant information dictionary object returned by openmano vimconnector
178 driver into Protobuf Gi Object
181 tenant_info - tenant information dictionary object
186 tenant
= RwcalYang
.TenantInfoItem()
187 tenant
.tenant_name
= tenant_info
['name']
188 tenant
.tenant_id
= tenant_info
['id']
191 @rwstatus(ret_on_failure
=[[]])
192 def do_get_tenant_list(self
, account
):
193 response
= RwcalYang
.VimResources()
194 with self
._use
_driver
(account
) as drv
:
195 tenants
= drv
.get_tenant_list()
196 for tenant
in tenants
:
197 response
.tenantinfo_list
.append(RwcalOpenmanoVimConnector
._fill
_tenant
_info
(tenant
))
201 def do_create_role(self
, account
, name
):
202 raise NotImplementedError()
205 def do_delete_role(self
, account
, role_id
):
206 raise NotImplementedError()
208 @rwstatus(ret_on_failure
=[[]])
209 def do_get_role_list(self
, account
):
210 raise NotImplementedError()
212 @rwstatus(ret_on_failure
=[None])
213 def do_create_image(self
, account
, image
):
214 with self
._use
_driver
(account
) as drv
:
216 # If the use passed in a file descriptor, use that to
218 if image
.has_field("fileno"):
219 new_fileno
= os
.dup(image
.fileno
)
220 hdl
= os
.fdopen(new_fileno
, 'rb')
222 hdl
= open(image
.location
, "rb")
223 except Exception as e
:
224 self
.log
.error("Could not open file for upload. Exception received: %s", str(e
))
227 tpt
= paramiko
.Transport((account
.openvim
.host
, 22))
229 tpt
.connect(username
=account
.openvim
.image_management
.username
,
230 password
=account
.openvim
.image_management
.password
)
231 except Exception as e
:
232 self
.log
.error('Could not connect to openvim host: %s. Exception: %s', account
.openvim
.host
, e
)
235 sftp
= paramiko
.SFTPClient
.from_transport(tpt
)
236 destination
= account
.openvim
.image_management
.image_directory_path
.rstrip('/')+'/'+image
.name
239 sftp
.putfo(fd
, destination
)
240 except Exception as e
:
241 self
.log
.warn('*** Caught exception: %s: %s', e
.__class
__, e
)
247 image_dict
['name'] = image
.name
248 image_dict
['location'] = destination
249 image_id
= drv
.new_image(image_dict
)
253 def do_delete_image(self
, account
, image_id
):
254 with self
._use
_driver
(account
) as drv
:
255 drv
.delete_image(image_id
)
258 def _fill_image_info(img_info
):
259 img
= RwcalYang
.ImageInfoItem()
260 img
.name
= img_info
['name']
261 img
.id = img_info
['id']
262 img
.location
= img_info
['path']
263 if img_info
['status'] == 'ACTIVE':
266 img
.state
= 'inactive'
269 @rwstatus(ret_on_failure
=[None])
270 def do_get_image(self
, account
, image_id
):
271 with self
._use
_driver
(account
) as drv
:
272 image
= drv
.get_image(image_id
)
273 return RwcalOpenmanoVimConnector
._fill
_image
_info
(image
)
275 @rwstatus(ret_on_failure
=[[]])
276 def do_get_image_list(self
, account
):
277 response
= RwcalYang
.VimResources()
278 with self
._use
_driver
(account
) as drv
:
279 images
= drv
.get_image_list()
281 image_info
= drv
.get_image(img
['id'])
282 response
.imageinfo_list
.append(RwcalOpenmanoVimConnector
._fill
_image
_info
(image_info
))
286 def do_create_vm(self
, account
, vm
):
287 raise NotImplementedError()
290 def do_start_vm(self
, account
, vm_id
):
291 raise NotImplementedError()
294 def do_stop_vm(self
, account
, vm_id
):
295 raise NotImplementedError()
298 def do_delete_vm(self
, account
, vm_id
):
299 raise NotImplementedError()
302 def do_reboot_vm(self
, account
, vm_id
):
303 raise NotImplementedError()
305 @rwstatus(ret_on_failure
=[[]])
306 def do_get_vm_list(self
, account
):
307 return RwcalYang
.VimResources()
309 def _fill_flavor_create_attributes(flavor
):
311 flavor_dict
['name'] = flavor
.name
312 flavor_dict
['ram'] = flavor
.vm_flavor
.memory_mb
313 flavor_dict
['disk'] = flavor
.vm_flavor
.storage_gb
314 flavor_dict
['vcpus'] = flavor
.vm_flavor
.vcpu_count
318 def do_create_flavor(self
, account
, flavor
):
319 with self
._use
_driver
(account
) as drv
:
320 flavor_dict
= RwcalOpenmanoVimConnector
._fill
_flavor
_create
_attributes
(flavor
)
321 flavor_id
= drv
.new_flavor(flavor_dict
)
325 def do_delete_flavor(self
, account
, flavor_id
):
326 with self
._use
_driver
(account
) as drv
:
327 drv
.delete_flavor(flavor_id
)
330 def _fill_epa_attributes(flavor
, flavor_info
):
331 if 'ram' in flavor_info
and flavor_info
['ram']:
332 getattr(flavor
, 'vm_flavor').memory_mb
= flavor_info
.get('ram',0)
333 if 'disk' in flavor_info
and flavor_info
['disk']:
334 getattr(flavor
, 'vm_flavor').storage_gb
= flavor_info
.get('disk',0)
335 if 'vcpus' in flavor_info
and flavor_info
['vcpus']:
336 getattr(flavor
, 'vm_flavor').vcpu_count
= flavor_info
.get('vcpus',0)
338 if not 'extended' in flavor_info
or flavor_info
['extended'] is None:
340 getattr(flavor
,'guest_epa').numa_node_policy
.node_cnt
= len(flavor_info
['extended']['numas'])
341 for attr
in flavor_info
['extended']['numas']:
342 numa_node
= getattr(flavor
,'guest_epa').numa_node_policy
.node
.add()
343 numa_node
.memory_mb
= attr
.get('memory',0)*1024
344 #getattr(flavor, 'host_epa').cpu_core_thread_count =
347 def _fill_flavor_info(flavor_info
):
348 flavor
= RwcalYang
.FlavorInfoItem()
349 flavor
.name
= flavor_info
['name']
350 flavor
.id = flavor_info
['id']
351 RwcalOpenmanoVimConnector
._fill
_epa
_attributes
(flavor
, flavor_info
)
354 @rwstatus(ret_on_failure
=[None])
355 def do_get_flavor(self
, account
, flavor_id
):
356 with self
._use
_driver
(account
) as drv
:
357 flavor
= drv
.get_flavor(flavor_id
)
358 return RwcalOpenmanoVimConnector
._fill
_flavor
_info
(flavor
)
361 @rwstatus(ret_on_failure
=[[]])
362 def do_get_flavor_list(self
, account
):
363 response
= RwcalYang
.VimResources()
364 with self
._use
_driver
(account
) as drv
:
365 flavors
= drv
.get_flavor_list()
367 flav_info
= drv
.get_flavor(flav
['id'])
368 response
.flavorinfo_list
.append(RwcalOpenmanoVimConnector
._fill
_flavor
_info
(flav_info
))
372 def do_add_host(self
, account
, host
):
373 raise NotImplementedError()
376 def do_remove_host(self
, account
, host_id
):
377 raise NotImplementedError()
379 @rwstatus(ret_on_failure
=[None])
380 def do_get_host(self
, account
, host_id
):
381 raise NotImplementedError()
383 @rwstatus(ret_on_failure
=[[]])
384 def do_get_host_list(self
, account
):
385 raise NotImplementedError()
388 def do_create_port(self
, account
, port
):
389 raise NotImplementedError()
392 def do_delete_port(self
, account
, port_id
):
393 raise NotImplementedError()
395 @rwstatus(ret_on_failure
=[None])
396 def do_get_port(self
, account
, port_id
):
397 raise NotImplementedError()
399 @rwstatus(ret_on_failure
=[[]])
400 def do_get_port_list(self
, account
):
401 return RwcalYang
.VimResources()
404 def do_create_network(self
, account
, network
):
405 with self
._use
_driver
(account
) as drv
:
406 network_id
= drv
.new_network(network
.name
,'bridge_man')
410 def do_delete_network(self
, account
, network_id
):
411 with self
._use
_driver
(account
) as drv
:
412 drv
.delete_network(network_id
)
414 def _fill_network_info(self
, network_info
):
415 network
= RwcalYang
.NetworkInfoItem()
416 network
.network_name
= network_info
['name']
417 network
.network_id
= network_info
['id']
418 if ('provider:physical' in network_info
) and (network_info
['provider:physical']):
419 network
.provider_network
.physical_network
= network_info
['provider:physical'].upper()
420 if ('provider:vlan' in network_info
) and (network_info
['provider:vlan']):
421 network
.provider_network
.segmentation_id
= network_info
['provider:vlan']
422 network
.provider_network
.overlay_type
= 'vlan'
425 @rwstatus(ret_on_failure
=[None])
426 def do_get_network(self
, account
, network_id
):
427 with self
._use
_driver
(account
) as drv
:
428 network
= drv
.get_network(id)
429 return self
._fill
_network
_info
(network
)
431 @rwstatus(ret_on_failure
=[[]])
432 def do_get_network_list(self
, account
):
433 response
= RwcalYang
.VimResources()
434 with self
._use
_driver
(account
) as drv
:
435 networks
= drv
.get_network_list()
436 for network
in networks
:
437 response
.networkinfo_list
.append(self
._fill
_network
_info
(network
))
440 @rwcalstatus(ret_on_failure
=[""])
441 def do_create_virtual_link(self
, account
, link_params
):
442 with self
._use
_driver
(account
) as drv
:
444 if link_params
.provider_network
.physical_network
is not None:
445 net
['provider:physical'] = link_params
.provider_network
.physical_network
447 # net['provider:physical'] = 'default'
448 if link_params
.provider_network
.overlay_type
== 'VLAN' and link_params
.provider_network
.segmentation_id
:
449 net
['provider:vlan'] = link_params
.provider_network
.segmentation_id
450 network_id
= drv
.new_network(link_params
.name
,'bridge_man',shared
=False,**net
)
454 def do_delete_virtual_link(self
, account
, link_id
):
455 with self
._use
_driver
(account
) as drv
:
456 drv
.delete_network(link_id
)
460 def _fill_connection_point_info(c_point
, port_info
):
461 c_point
.name
= port_info
['name']
462 c_point
.connection_point_id
= port_info
['id']
463 if 'ip_address' in port_info
:
464 c_point
.ip_address
= port_info
['ip_address']
465 if port_info
['status'] == 'ACTIVE':
466 c_point
.state
= 'active'
468 c_point
.state
= 'inactive'
469 if 'network_id' in port_info
:
470 c_point
.virtual_link_id
= port_info
['network_id']
471 if ('device_id' in port_info
) and (port_info
['device_id']):
472 c_point
.vdu_id
= port_info
['device_id']
474 def _fill_virtual_link_info(self
, drv
, network_info
):
475 link
= RwcalYang
.VirtualLinkInfoParams()
476 link
.name
= network_info
['name']
477 link
.virtual_link_id
= network_info
['id']
478 if network_info
['admin_state_up']:
479 link
.state
= 'active'
481 link
.state
= 'inactive'
482 link
.virtual_link_id
= network_info
['id']
483 if ('provider:physical' in network_info
) and (network_info
['provider:physical']):
484 link
.provider_network
.physical_network
= network_info
['provider:physical']
485 if ('provider:vlan' in network_info
) and (network_info
['provider:vlan']):
486 link
.provider_network
.segmentation_id
= network_info
['provider:vlan']
487 link
.provider_network
.overlay_type
= 'VLAN'
489 if 'ports' in network_info
:
490 for port
in network_info
['ports']:
491 if 'port_id' in port
:
492 port_id
= port
['port_id']
493 port
= drv
.get_port(port_id
)
494 c_point
= link
.connection_points
.add()
495 RwcalOpenmanoVimConnector
._fill
_connection
_point
_info
(c_point
, port
)
498 @rwstatus(ret_on_failure
=[None])
499 def do_get_virtual_link(self
, account
, link_id
):
500 with self
._use
_driver
(account
) as drv
:
501 network
= drv
.get_network(link_id
)
502 return self
._fill
_virtual
_link
_info
(drv
,network
)
504 @rwstatus(ret_on_failure
=[""])
505 def do_get_virtual_link_list(self
, account
):
506 response
= RwcalYang
.VNFResources()
507 with self
._use
_driver
(account
) as drv
:
508 networks
= drv
.get_network_list()
509 for network
in networks
:
510 network_info
= drv
.get_network(network
['id'])
511 response
.virtual_link_info_list
.append(self
._fill
_virtual
_link
_info
(drv
,network_info
))
514 def _match_vm_flavor(self
, required
, available
):
515 self
.log
.info("Matching VM Flavor attributes required {}, available {}".format(required
, available
))
516 if available
.vcpu_count
!= required
.vcpu_count
:
518 if available
.memory_mb
!= required
.memory_mb
:
520 if available
.storage_gb
!= required
.storage_gb
:
522 self
.log
.debug("VM Flavor match found")
526 def _select_resource_flavor(self
, account
, vdu_init
):
528 Select a existing flavor if it matches the request or create new flavor
530 flavor
= RwcalYang
.FlavorInfoItem()
531 flavor
.name
= str(uuid
.uuid4())
532 epa_types
= ['vm_flavor', 'guest_epa', 'host_epa', 'host_aggregate', 'hypervisor_epa', 'vswitch_epa']
533 epa_dict
= {k
: v
for k
, v
in vdu_init
.as_dict().items() if k
in epa_types
}
534 flavor
.from_dict(epa_dict
)
536 rc
, response
= self
.do_get_flavor_list(account
)
537 if rc
!= RwTypes
.RwStatus
.SUCCESS
:
538 self
.log
.error("Get-flavor-info-list operation failed for cloud account: %s",
540 raise OpenvimCALOperationFailure("Get-flavor-info-list operation failed for cloud account: %s" %(account
.name
))
543 flavor_list
= response
.flavorinfo_list
544 self
.log
.debug("Received %d flavor information from RW.CAL", len(flavor_list
))
545 for flv
in flavor_list
:
546 self
.log
.info("Attempting to match compute requirement for VDU: %s with flavor %s",
548 if self
._match
_vm
_flavor
(flavor
.vm_flavor
,flv
.vm_flavor
):
549 self
.log
.info("Flavor match found for compute requirements for VDU: %s with flavor name: %s, flavor-id: %s",
550 vdu_init
.name
, flv
.name
, flv
.id)
553 if account
.openvim
.dynamic_flavor_support
is False:
554 self
.log
.error("Unable to create flavor for compute requirement for VDU: %s. VDU instantiation failed", vdu_init
.name
)
555 raise OpenvimCALOperationFailure("No resource available with matching EPA attributes")
557 rc
,flavor_id
= self
.do_create_flavor(account
,flavor
)
558 if rc
!= RwTypes
.RwStatus
.SUCCESS
:
559 self
.log
.error("Create-flavor operation failed for cloud account: %s",
561 raise OpenvimCALOperationFailure("Create-flavor operation failed for cloud account: %s" %(account
.name
))
565 @rwcalstatus(ret_on_failure
=[""])
566 def do_create_vdu(self
, account
, vdu_init
):
567 with self
._use
_driver
(account
) as drv
:
570 if not vdu_init
.has_field('flavor_id'):
571 vdu_init
.flavor_id
= self
._select
_resource
_flavor
(account
,vdu_init
)
573 if account
.openvim
.mgmt_network
:
574 mgmt_net_list
= drv
.get_network_list()
575 mgmt_net_id
= [net
['id'] for net
in mgmt_net_list
if net
['name'] == account
.openvim
.mgmt_network
]
576 if len(mgmt_net_id
) > 0:
578 mgmt_net_dict
['name'] = account
.openvim
.mgmt_network
579 mgmt_net_dict
['net_id'] = mgmt_net_id
[0]
580 mgmt_net_dict
['type'] = 'virtual'
581 net_list
.append(mgmt_net_dict
)
583 for c_point
in vdu_init
.connection_points
:
585 net_dict
['name'] = c_point
.name
586 net_dict
['net_id'] = c_point
.virtual_link_id
587 net_dict
['type'] = 'virtual'
588 net_list
.append(net_dict
)
590 vm_id
= drv
.new_vminstance(vdu_init
.name
,vdu_init
.name
,None,vdu_init
.image_id
,vdu_init
.flavor_id
,net_list
);
594 def do_modify_vdu(self
, account
, vdu_modify
):
598 def do_delete_vdu(self
, account
, vdu_id
):
600 self
.log
.error("empty vdu_id during the vdu deletion")
603 with self
._use
_driver
(account
) as drv
:
604 drv
.delete_vminstance(vdu_id
)
607 def _fill_vdu_info(drv
,account
,vm_info
):
608 vdu
= RwcalYang
.VDUInfoParams()
609 vdu
.name
= vm_info
['name']
610 vdu
.vdu_id
= vm_info
['id']
612 if ('image' in vm_info
) and ('id' in vm_info
['image']):
613 vdu
.image_id
= vm_info
['image']['id']
614 if ('flavor' in vm_info
) and ('id' in vm_info
['flavor']):
615 vdu
.flavor_id
= vm_info
['flavor']['id']
616 vdu
.cloud_type
= 'openvim'
618 if account
.openvim
.mgmt_network
:
619 net_list
= drv
.get_network_list()
620 mgmt_net_list
= [net
['id'] for net
in net_list
if net
['name'] == account
.openvim
.mgmt_network
]
621 if len(mgmt_net_list
) > 0:
622 mgmt_net_id
= mgmt_net_list
[0]
624 if 'networks' in vm_info
:
625 for network
in vm_info
['networks']:
626 port_id
= network
['iface_id']
627 port
= drv
.get_port(port_id
)
628 if 'network_id' in port
and mgmt_net_id
== port
['network_id'] and 'ip_address' in port
:
629 vdu
.management_ip
= port
['ip_address']
630 vdu
.public_ip
= vdu
.management_ip
632 c_point
= vdu
.connection_points
.add()
633 RwcalOpenmanoVimConnector
._fill
_connection
_point
_info
(c_point
, port
)
636 if vm_info
['status'] == 'ACTIVE' and vdu
.management_ip
!= '':
638 elif vm_info
['status'] == 'ERROR':
641 vdu
.state
= 'inactive'
644 flavor
= drv
.get_flavor(vdu
.flavor_id
)
645 RwcalOpenmanoVimConnector
._fill
_epa
_attributes
(vdu
, flavor
)
648 @rwstatus(ret_on_failure
=[None])
649 def do_get_vdu(self
, account
, vdu_id
):
650 with self
._use
_driver
(account
) as drv
:
651 vm_info
= drv
.get_vminstance(vdu_id
)
652 return RwcalOpenmanoVimConnector
._fill
_vdu
_info
(drv
,account
,vm_info
)
654 @rwstatus(ret_on_failure
=[""])
655 def do_get_vdu_list(self
, account
):
656 vnf_resource
= RwcalYang
.VNFResources()
657 with self
._use
_driver
(account
) as drv
:
658 vms
= drv
.get_vminstance_list()
660 vm_info
= drv
.get_vminstance(vm
['id'])
661 vdu
= RwcalOpenmanoVimConnector
._fill
_vdu
_info
(drv
,account
,vm_info
)
662 vnf_resource
.vdu_info_list
.append(vdu
)