Convert vmwareapi code to UNIX style line endings
It's the only code in the codebase using evil CRLF line endings. Change-Id: I8b1a2b12a5707fbd4d32588c081599beaa34aca5
This commit is contained in:
parent
99daaea663
commit
50f3198477
@ -1,21 +1,21 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
:mod:`vmwareapi` -- Stubs for VMware API
|
||||
=======================================
|
||||
"""
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
:mod:`vmwareapi` -- Stubs for VMware API
|
||||
=======================================
|
||||
"""
|
||||
|
@ -1,109 +1,109 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Stubouts, mocks and fixtures for the test suite
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from nova import db
|
||||
from nova import utils
|
||||
from nova.compute import task_states
|
||||
from nova.compute import vm_states
|
||||
|
||||
|
||||
def stub_out_db_instance_api(stubs):
|
||||
"""Stubs out the db API for creating Instances."""
|
||||
|
||||
INSTANCE_TYPES = {
|
||||
'm1.tiny': dict(memory_mb=512, vcpus=1, root_gb=0, flavorid=1),
|
||||
'm1.small': dict(memory_mb=2048, vcpus=1, root_gb=20, flavorid=2),
|
||||
'm1.medium':
|
||||
dict(memory_mb=4096, vcpus=2, root_gb=40, flavorid=3),
|
||||
'm1.large': dict(memory_mb=8192, vcpus=4, root_gb=80, flavorid=4),
|
||||
'm1.xlarge':
|
||||
dict(memory_mb=16384, vcpus=8, root_gb=160, flavorid=5)}
|
||||
|
||||
class FakeModel(object):
|
||||
"""Stubs out for model."""
|
||||
|
||||
def __init__(self, values):
|
||||
self.values = values
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self.values[name]
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key in self.values:
|
||||
return self.values[key]
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
def fake_instance_create(context, values):
|
||||
"""Stubs out the db.instance_create method."""
|
||||
|
||||
type_data = INSTANCE_TYPES[values['instance_type']]
|
||||
|
||||
base_options = {
|
||||
'name': values['name'],
|
||||
'id': values['id'],
|
||||
'uuid': utils.gen_uuid(),
|
||||
'reservation_id': utils.generate_uid('r'),
|
||||
'image_ref': values['image_ref'],
|
||||
'kernel_id': values['kernel_id'],
|
||||
'ramdisk_id': values['ramdisk_id'],
|
||||
'vm_state': vm_states.BUILDING,
|
||||
'task_state': task_states.SCHEDULING,
|
||||
'user_id': values['user_id'],
|
||||
'project_id': values['project_id'],
|
||||
'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
|
||||
'instance_type': values['instance_type'],
|
||||
'memory_mb': type_data['memory_mb'],
|
||||
'vcpus': type_data['vcpus'],
|
||||
'mac_addresses': [{'address': values['mac_address']}],
|
||||
'root_gb': type_data['root_gb'],
|
||||
}
|
||||
return FakeModel(base_options)
|
||||
|
||||
def fake_network_get_by_instance(context, instance_id):
|
||||
"""Stubs out the db.network_get_by_instance method."""
|
||||
|
||||
fields = {
|
||||
'bridge': 'vmnet0',
|
||||
'netmask': '255.255.255.0',
|
||||
'gateway': '10.10.10.1',
|
||||
'broadcast': '10.10.10.255',
|
||||
'dns1': 'fake',
|
||||
'vlan': 100}
|
||||
return FakeModel(fields)
|
||||
|
||||
def fake_instance_action_create(context, action):
|
||||
"""Stubs out the db.instance_action_create method."""
|
||||
pass
|
||||
|
||||
def fake_instance_type_get_all(context, inactive=0, filters=None):
|
||||
return INSTANCE_TYPES.values()
|
||||
|
||||
def fake_instance_type_get_by_name(context, name):
|
||||
return INSTANCE_TYPES[name]
|
||||
|
||||
stubs.Set(db, 'instance_create', fake_instance_create)
|
||||
stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance)
|
||||
stubs.Set(db, 'instance_action_create', fake_instance_action_create)
|
||||
stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all)
|
||||
stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name)
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Stubouts, mocks and fixtures for the test suite
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from nova import db
|
||||
from nova import utils
|
||||
from nova.compute import task_states
|
||||
from nova.compute import vm_states
|
||||
|
||||
|
||||
def stub_out_db_instance_api(stubs):
|
||||
"""Stubs out the db API for creating Instances."""
|
||||
|
||||
INSTANCE_TYPES = {
|
||||
'm1.tiny': dict(memory_mb=512, vcpus=1, root_gb=0, flavorid=1),
|
||||
'm1.small': dict(memory_mb=2048, vcpus=1, root_gb=20, flavorid=2),
|
||||
'm1.medium':
|
||||
dict(memory_mb=4096, vcpus=2, root_gb=40, flavorid=3),
|
||||
'm1.large': dict(memory_mb=8192, vcpus=4, root_gb=80, flavorid=4),
|
||||
'm1.xlarge':
|
||||
dict(memory_mb=16384, vcpus=8, root_gb=160, flavorid=5)}
|
||||
|
||||
class FakeModel(object):
|
||||
"""Stubs out for model."""
|
||||
|
||||
def __init__(self, values):
|
||||
self.values = values
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self.values[name]
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key in self.values:
|
||||
return self.values[key]
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
def fake_instance_create(context, values):
|
||||
"""Stubs out the db.instance_create method."""
|
||||
|
||||
type_data = INSTANCE_TYPES[values['instance_type']]
|
||||
|
||||
base_options = {
|
||||
'name': values['name'],
|
||||
'id': values['id'],
|
||||
'uuid': utils.gen_uuid(),
|
||||
'reservation_id': utils.generate_uid('r'),
|
||||
'image_ref': values['image_ref'],
|
||||
'kernel_id': values['kernel_id'],
|
||||
'ramdisk_id': values['ramdisk_id'],
|
||||
'vm_state': vm_states.BUILDING,
|
||||
'task_state': task_states.SCHEDULING,
|
||||
'user_id': values['user_id'],
|
||||
'project_id': values['project_id'],
|
||||
'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
|
||||
'instance_type': values['instance_type'],
|
||||
'memory_mb': type_data['memory_mb'],
|
||||
'vcpus': type_data['vcpus'],
|
||||
'mac_addresses': [{'address': values['mac_address']}],
|
||||
'root_gb': type_data['root_gb'],
|
||||
}
|
||||
return FakeModel(base_options)
|
||||
|
||||
def fake_network_get_by_instance(context, instance_id):
|
||||
"""Stubs out the db.network_get_by_instance method."""
|
||||
|
||||
fields = {
|
||||
'bridge': 'vmnet0',
|
||||
'netmask': '255.255.255.0',
|
||||
'gateway': '10.10.10.1',
|
||||
'broadcast': '10.10.10.255',
|
||||
'dns1': 'fake',
|
||||
'vlan': 100}
|
||||
return FakeModel(fields)
|
||||
|
||||
def fake_instance_action_create(context, action):
|
||||
"""Stubs out the db.instance_action_create method."""
|
||||
pass
|
||||
|
||||
def fake_instance_type_get_all(context, inactive=0, filters=None):
|
||||
return INSTANCE_TYPES.values()
|
||||
|
||||
def fake_instance_type_get_by_name(context, name):
|
||||
return INSTANCE_TYPES[name]
|
||||
|
||||
stubs.Set(db, 'instance_create', fake_instance_create)
|
||||
stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance)
|
||||
stubs.Set(db, 'instance_action_create', fake_instance_action_create)
|
||||
stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all)
|
||||
stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name)
|
||||
|
@ -1,51 +1,51 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Stubouts for the test suite
|
||||
"""
|
||||
|
||||
from nova.virt import vmwareapi_conn
|
||||
from nova.virt.vmwareapi import fake
|
||||
from nova.virt.vmwareapi import vmware_images
|
||||
from nova.virt.vmwareapi import vmops
|
||||
from nova.virt.vmwareapi import network_utils
|
||||
|
||||
|
||||
def fake_get_vim_object(arg):
|
||||
"""Stubs out the VMWareAPISession's get_vim_object method."""
|
||||
return fake.FakeVim()
|
||||
|
||||
|
||||
def fake_is_vim_object(arg, module):
|
||||
"""Stubs out the VMWareAPISession's is_vim_object method."""
|
||||
return isinstance(module, fake.FakeVim)
|
||||
|
||||
|
||||
def set_stubs(stubs):
|
||||
"""Set the stubs."""
|
||||
stubs.Set(vmops.VMWareVMOps, 'plug_vifs', fake.fake_plug_vifs)
|
||||
stubs.Set(network_utils, 'get_network_with_the_name',
|
||||
fake.fake_get_network)
|
||||
stubs.Set(vmware_images, 'fetch_image', fake.fake_fetch_image)
|
||||
stubs.Set(vmware_images, 'get_vmdk_size_and_properties',
|
||||
fake.fake_get_vmdk_size_and_properties)
|
||||
stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image)
|
||||
stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object",
|
||||
fake_get_vim_object)
|
||||
stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object",
|
||||
fake_is_vim_object)
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Stubouts for the test suite
|
||||
"""
|
||||
|
||||
from nova.virt import vmwareapi_conn
|
||||
from nova.virt.vmwareapi import fake
|
||||
from nova.virt.vmwareapi import vmware_images
|
||||
from nova.virt.vmwareapi import vmops
|
||||
from nova.virt.vmwareapi import network_utils
|
||||
|
||||
|
||||
def fake_get_vim_object(arg):
|
||||
"""Stubs out the VMWareAPISession's get_vim_object method."""
|
||||
return fake.FakeVim()
|
||||
|
||||
|
||||
def fake_is_vim_object(arg, module):
|
||||
"""Stubs out the VMWareAPISession's is_vim_object method."""
|
||||
return isinstance(module, fake.FakeVim)
|
||||
|
||||
|
||||
def set_stubs(stubs):
|
||||
"""Set the stubs."""
|
||||
stubs.Set(vmops.VMWareVMOps, 'plug_vifs', fake.fake_plug_vifs)
|
||||
stubs.Set(network_utils, 'get_network_with_the_name',
|
||||
fake.fake_get_network)
|
||||
stubs.Set(vmware_images, 'fetch_image', fake.fake_fetch_image)
|
||||
stubs.Set(vmware_images, 'get_vmdk_size_and_properties',
|
||||
fake.fake_get_vmdk_size_and_properties)
|
||||
stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image)
|
||||
stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object",
|
||||
fake_get_vim_object)
|
||||
stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object",
|
||||
fake_is_vim_object)
|
||||
|
@ -1,96 +1,96 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Exception classes and SOAP response error checking module.
|
||||
"""
|
||||
|
||||
FAULT_NOT_AUTHENTICATED = "NotAuthenticated"
|
||||
FAULT_ALREADY_EXISTS = "AlreadyExists"
|
||||
|
||||
|
||||
class VimException(Exception):
|
||||
"""The VIM Exception class."""
|
||||
|
||||
def __init__(self, exception_summary, excep):
|
||||
Exception.__init__(self)
|
||||
self.exception_summary = exception_summary
|
||||
self.exception_obj = excep
|
||||
|
||||
def __str__(self):
|
||||
return self.exception_summary + str(self.exception_obj)
|
||||
|
||||
|
||||
class SessionOverLoadException(VimException):
|
||||
"""Session Overload Exception."""
|
||||
pass
|
||||
|
||||
|
||||
class VimAttributeError(VimException):
|
||||
"""VI Attribute Error."""
|
||||
pass
|
||||
|
||||
|
||||
class VimFaultException(Exception):
|
||||
"""The VIM Fault exception class."""
|
||||
|
||||
def __init__(self, fault_list, excep):
|
||||
Exception.__init__(self)
|
||||
self.fault_list = fault_list
|
||||
self.exception_obj = excep
|
||||
|
||||
def __str__(self):
|
||||
return str(self.exception_obj)
|
||||
|
||||
|
||||
class FaultCheckers(object):
|
||||
"""
|
||||
Methods for fault checking of SOAP response. Per Method error handlers
|
||||
for which we desire error checking are defined. SOAP faults are
|
||||
embedded in the SOAP messages as properties and not as SOAP faults.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def retrieveproperties_fault_checker(resp_obj):
|
||||
"""
|
||||
Checks the RetrieveProperties response for errors. Certain faults
|
||||
are sent as part of the SOAP body as property of missingSet.
|
||||
For example NotAuthenticated fault.
|
||||
"""
|
||||
fault_list = []
|
||||
if not resp_obj:
|
||||
# This is the case when the session has timed out. ESX SOAP server
|
||||
# sends an empty RetrievePropertiesResponse. Normally missingSet in
|
||||
# the returnval field has the specifics about the error, but that's
|
||||
# not the case with a timed out idle session. It is as bad as a
|
||||
# terminated session for we cannot use the session. So setting
|
||||
# fault to NotAuthenticated fault.
|
||||
fault_list = ["NotAuthenticated"]
|
||||
else:
|
||||
for obj_cont in resp_obj:
|
||||
if hasattr(obj_cont, "missingSet"):
|
||||
for missing_elem in obj_cont.missingSet:
|
||||
fault_type = \
|
||||
missing_elem.fault.fault.__class__.__name__
|
||||
# Fault needs to be added to the type of fault for
|
||||
# uniformity in error checking as SOAP faults define
|
||||
fault_list.append(fault_type)
|
||||
if fault_list:
|
||||
exc_msg_list = ', '.join(fault_list)
|
||||
raise VimFaultException(fault_list, Exception(_("Error(s) %s "
|
||||
"occurred in the call to RetrieveProperties") %
|
||||
exc_msg_list))
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Exception classes and SOAP response error checking module.
|
||||
"""
|
||||
|
||||
FAULT_NOT_AUTHENTICATED = "NotAuthenticated"
|
||||
FAULT_ALREADY_EXISTS = "AlreadyExists"
|
||||
|
||||
|
||||
class VimException(Exception):
|
||||
"""The VIM Exception class."""
|
||||
|
||||
def __init__(self, exception_summary, excep):
|
||||
Exception.__init__(self)
|
||||
self.exception_summary = exception_summary
|
||||
self.exception_obj = excep
|
||||
|
||||
def __str__(self):
|
||||
return self.exception_summary + str(self.exception_obj)
|
||||
|
||||
|
||||
class SessionOverLoadException(VimException):
|
||||
"""Session Overload Exception."""
|
||||
pass
|
||||
|
||||
|
||||
class VimAttributeError(VimException):
|
||||
"""VI Attribute Error."""
|
||||
pass
|
||||
|
||||
|
||||
class VimFaultException(Exception):
|
||||
"""The VIM Fault exception class."""
|
||||
|
||||
def __init__(self, fault_list, excep):
|
||||
Exception.__init__(self)
|
||||
self.fault_list = fault_list
|
||||
self.exception_obj = excep
|
||||
|
||||
def __str__(self):
|
||||
return str(self.exception_obj)
|
||||
|
||||
|
||||
class FaultCheckers(object):
|
||||
"""
|
||||
Methods for fault checking of SOAP response. Per Method error handlers
|
||||
for which we desire error checking are defined. SOAP faults are
|
||||
embedded in the SOAP messages as properties and not as SOAP faults.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def retrieveproperties_fault_checker(resp_obj):
|
||||
"""
|
||||
Checks the RetrieveProperties response for errors. Certain faults
|
||||
are sent as part of the SOAP body as property of missingSet.
|
||||
For example NotAuthenticated fault.
|
||||
"""
|
||||
fault_list = []
|
||||
if not resp_obj:
|
||||
# This is the case when the session has timed out. ESX SOAP server
|
||||
# sends an empty RetrievePropertiesResponse. Normally missingSet in
|
||||
# the returnval field has the specifics about the error, but that's
|
||||
# not the case with a timed out idle session. It is as bad as a
|
||||
# terminated session for we cannot use the session. So setting
|
||||
# fault to NotAuthenticated fault.
|
||||
fault_list = ["NotAuthenticated"]
|
||||
else:
|
||||
for obj_cont in resp_obj:
|
||||
if hasattr(obj_cont, "missingSet"):
|
||||
for missing_elem in obj_cont.missingSet:
|
||||
fault_type = \
|
||||
missing_elem.fault.fault.__class__.__name__
|
||||
# Fault needs to be added to the type of fault for
|
||||
# uniformity in error checking as SOAP faults define
|
||||
fault_list.append(fault_type)
|
||||
if fault_list:
|
||||
exc_msg_list = ', '.join(fault_list)
|
||||
raise VimFaultException(fault_list, Exception(_("Error(s) %s "
|
||||
"occurred in the call to RetrieveProperties") %
|
||||
exc_msg_list))
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,169 +1,169 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Utility classes for defining the time saving transfer of data from the reader
|
||||
to the write using a LightQueue as a Pipe between the reader and the writer.
|
||||
"""
|
||||
|
||||
from eventlet import event
|
||||
from eventlet import greenthread
|
||||
from eventlet.queue import LightQueue
|
||||
|
||||
from nova import exception
|
||||
from nova import log as logging
|
||||
|
||||
LOG = logging.getLogger("nova.virt.vmwareapi.io_util")
|
||||
|
||||
IO_THREAD_SLEEP_TIME = .01
|
||||
GLANCE_POLL_INTERVAL = 5
|
||||
|
||||
|
||||
class ThreadSafePipe(LightQueue):
|
||||
"""The pipe to hold the data which the reader writes to and the writer
|
||||
reads from."""
|
||||
|
||||
def __init__(self, maxsize, transfer_size):
|
||||
LightQueue.__init__(self, maxsize)
|
||||
self.transfer_size = transfer_size
|
||||
self.transferred = 0
|
||||
|
||||
def read(self, chunk_size):
|
||||
"""Read data from the pipe. Chunksize if ignored for we have ensured
|
||||
that the data chunks written to the pipe by readers is the same as the
|
||||
chunks asked for by the Writer."""
|
||||
if self.transferred < self.transfer_size:
|
||||
data_item = self.get()
|
||||
self.transferred += len(data_item)
|
||||
return data_item
|
||||
else:
|
||||
return ""
|
||||
|
||||
def write(self, data):
|
||||
"""Put a data item in the pipe."""
|
||||
self.put(data)
|
||||
|
||||
def close(self):
|
||||
"""A place-holder to maintain consistency."""
|
||||
pass
|
||||
|
||||
|
||||
class GlanceWriteThread(object):
|
||||
"""Ensures that image data is written to in the glance client and that
|
||||
it is in correct ('active')state."""
|
||||
|
||||
def __init__(self, input, glance_client, image_id, image_meta=None):
|
||||
if not image_meta:
|
||||
image_meta = {}
|
||||
|
||||
self.input = input
|
||||
self.glance_client = glance_client
|
||||
self.image_id = image_id
|
||||
self.image_meta = image_meta
|
||||
self._running = False
|
||||
|
||||
def start(self):
|
||||
self.done = event.Event()
|
||||
|
||||
def _inner():
|
||||
"""Function to do the image data transfer through an update
|
||||
and thereon checks if the state is 'active'."""
|
||||
self.glance_client.update_image(self.image_id,
|
||||
image_meta=self.image_meta,
|
||||
image_data=self.input)
|
||||
self._running = True
|
||||
while self._running:
|
||||
try:
|
||||
image_status = \
|
||||
self.glance_client.get_image_meta(self.image_id).get(
|
||||
"status")
|
||||
if image_status == "active":
|
||||
self.stop()
|
||||
self.done.send(True)
|
||||
# If the state is killed, then raise an exception.
|
||||
elif image_status == "killed":
|
||||
self.stop()
|
||||
exc_msg = _("Glance image %s is in killed state") %\
|
||||
self.image_id
|
||||
LOG.exception(exc_msg)
|
||||
self.done.send_exception(exception.Error(exc_msg))
|
||||
elif image_status in ["saving", "queued"]:
|
||||
greenthread.sleep(GLANCE_POLL_INTERVAL)
|
||||
else:
|
||||
self.stop()
|
||||
exc_msg = _("Glance image "
|
||||
"%(image_id)s is in unknown state "
|
||||
"- %(state)s") % {
|
||||
"image_id": self.image_id,
|
||||
"state": image_status}
|
||||
LOG.exception(exc_msg)
|
||||
self.done.send_exception(exception.Error(exc_msg))
|
||||
except Exception, exc:
|
||||
self.stop()
|
||||
self.done.send_exception(exc)
|
||||
|
||||
greenthread.spawn(_inner)
|
||||
return self.done
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
|
||||
def wait(self):
|
||||
return self.done.wait()
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
class IOThread(object):
|
||||
"""Class that reads chunks from the input file and writes them to the
|
||||
output file till the transfer is completely done."""
|
||||
|
||||
def __init__(self, input, output):
|
||||
self.input = input
|
||||
self.output = output
|
||||
self._running = False
|
||||
self.got_exception = False
|
||||
|
||||
def start(self):
|
||||
self.done = event.Event()
|
||||
|
||||
def _inner():
|
||||
"""Read data from the input and write the same to the output
|
||||
until the transfer completes."""
|
||||
self._running = True
|
||||
while self._running:
|
||||
try:
|
||||
data = self.input.read(None)
|
||||
if not data:
|
||||
self.stop()
|
||||
self.done.send(True)
|
||||
self.output.write(data)
|
||||
greenthread.sleep(IO_THREAD_SLEEP_TIME)
|
||||
except Exception, exc:
|
||||
self.stop()
|
||||
LOG.exception(exc)
|
||||
self.done.send_exception(exc)
|
||||
|
||||
greenthread.spawn(_inner)
|
||||
return self.done
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
|
||||
def wait(self):
|
||||
return self.done.wait()
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Utility classes for defining the time saving transfer of data from the reader
|
||||
to the write using a LightQueue as a Pipe between the reader and the writer.
|
||||
"""
|
||||
|
||||
from eventlet import event
|
||||
from eventlet import greenthread
|
||||
from eventlet.queue import LightQueue
|
||||
|
||||
from nova import exception
|
||||
from nova import log as logging
|
||||
|
||||
LOG = logging.getLogger("nova.virt.vmwareapi.io_util")
|
||||
|
||||
IO_THREAD_SLEEP_TIME = .01
|
||||
GLANCE_POLL_INTERVAL = 5
|
||||
|
||||
|
||||
class ThreadSafePipe(LightQueue):
|
||||
"""The pipe to hold the data which the reader writes to and the writer
|
||||
reads from."""
|
||||
|
||||
def __init__(self, maxsize, transfer_size):
|
||||
LightQueue.__init__(self, maxsize)
|
||||
self.transfer_size = transfer_size
|
||||
self.transferred = 0
|
||||
|
||||
def read(self, chunk_size):
|
||||
"""Read data from the pipe. Chunksize if ignored for we have ensured
|
||||
that the data chunks written to the pipe by readers is the same as the
|
||||
chunks asked for by the Writer."""
|
||||
if self.transferred < self.transfer_size:
|
||||
data_item = self.get()
|
||||
self.transferred += len(data_item)
|
||||
return data_item
|
||||
else:
|
||||
return ""
|
||||
|
||||
def write(self, data):
|
||||
"""Put a data item in the pipe."""
|
||||
self.put(data)
|
||||
|
||||
def close(self):
|
||||
"""A place-holder to maintain consistency."""
|
||||
pass
|
||||
|
||||
|
||||
class GlanceWriteThread(object):
|
||||
"""Ensures that image data is written to in the glance client and that
|
||||
it is in correct ('active')state."""
|
||||
|
||||
def __init__(self, input, glance_client, image_id, image_meta=None):
|
||||
if not image_meta:
|
||||
image_meta = {}
|
||||
|
||||
self.input = input
|
||||
self.glance_client = glance_client
|
||||
self.image_id = image_id
|
||||
self.image_meta = image_meta
|
||||
self._running = False
|
||||
|
||||
def start(self):
|
||||
self.done = event.Event()
|
||||
|
||||
def _inner():
|
||||
"""Function to do the image data transfer through an update
|
||||
and thereon checks if the state is 'active'."""
|
||||
self.glance_client.update_image(self.image_id,
|
||||
image_meta=self.image_meta,
|
||||
image_data=self.input)
|
||||
self._running = True
|
||||
while self._running:
|
||||
try:
|
||||
image_status = \
|
||||
self.glance_client.get_image_meta(self.image_id).get(
|
||||
"status")
|
||||
if image_status == "active":
|
||||
self.stop()
|
||||
self.done.send(True)
|
||||
# If the state is killed, then raise an exception.
|
||||
elif image_status == "killed":
|
||||
self.stop()
|
||||
exc_msg = _("Glance image %s is in killed state") %\
|
||||
self.image_id
|
||||
LOG.exception(exc_msg)
|
||||
self.done.send_exception(exception.Error(exc_msg))
|
||||
elif image_status in ["saving", "queued"]:
|
||||
greenthread.sleep(GLANCE_POLL_INTERVAL)
|
||||
else:
|
||||
self.stop()
|
||||
exc_msg = _("Glance image "
|
||||
"%(image_id)s is in unknown state "
|
||||
"- %(state)s") % {
|
||||
"image_id": self.image_id,
|
||||
"state": image_status}
|
||||
LOG.exception(exc_msg)
|
||||
self.done.send_exception(exception.Error(exc_msg))
|
||||
except Exception, exc:
|
||||
self.stop()
|
||||
self.done.send_exception(exc)
|
||||
|
||||
greenthread.spawn(_inner)
|
||||
return self.done
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
|
||||
def wait(self):
|
||||
return self.done.wait()
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
class IOThread(object):
|
||||
"""Class that reads chunks from the input file and writes them to the
|
||||
output file till the transfer is completely done."""
|
||||
|
||||
def __init__(self, input, output):
|
||||
self.input = input
|
||||
self.output = output
|
||||
self._running = False
|
||||
self.got_exception = False
|
||||
|
||||
def start(self):
|
||||
self.done = event.Event()
|
||||
|
||||
def _inner():
|
||||
"""Read data from the input and write the same to the output
|
||||
until the transfer completes."""
|
||||
self._running = True
|
||||
while self._running:
|
||||
try:
|
||||
data = self.input.read(None)
|
||||
if not data:
|
||||
self.stop()
|
||||
self.done.send(True)
|
||||
self.output.write(data)
|
||||
greenthread.sleep(IO_THREAD_SLEEP_TIME)
|
||||
except Exception, exc:
|
||||
self.stop()
|
||||
LOG.exception(exc)
|
||||
self.done.send_exception(exc)
|
||||
|
||||
greenthread.spawn(_inner)
|
||||
return self.done
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
|
||||
def wait(self):
|
||||
return self.done.wait()
|
||||
|
@ -1,170 +1,170 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Utility functions for ESX Networking.
|
||||
"""
|
||||
|
||||
from nova import exception
|
||||
from nova import log as logging
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import vim_util
|
||||
from nova.virt.vmwareapi import vm_util
|
||||
|
||||
LOG = logging.getLogger("nova.virt.vmwareapi.network_utils")
|
||||
|
||||
|
||||
def get_network_with_the_name(session, network_name="vmnet0"):
|
||||
"""
|
||||
Gets reference to the network whose name is passed as the
|
||||
argument.
|
||||
"""
|
||||
hostsystems = session._call_method(vim_util, "get_objects",
|
||||
"HostSystem", ["network"])
|
||||
vm_networks_ret = hostsystems[0].propSet[0].val
|
||||
# Meaning there are no networks on the host. suds responds with a ""
|
||||
# in the parent property field rather than a [] in the
|
||||
# ManagedObjectRefernce property field of the parent
|
||||
if not vm_networks_ret:
|
||||
return None
|
||||
vm_networks = vm_networks_ret.ManagedObjectReference
|
||||
networks = session._call_method(vim_util,
|
||||
"get_properties_for_a_collection_of_objects",
|
||||
"Network", vm_networks, ["summary.name"])
|
||||
network_obj = {}
|
||||
LOG.warn(vm_networks)
|
||||
for network in vm_networks:
|
||||
# Get network properties
|
||||
if network._type == 'DistributedVirtualPortgroup':
|
||||
props = session._call_method(vim_util,
|
||||
"get_dynamic_property", network,
|
||||
"DistributedVirtualPortgroup", "config")
|
||||
# NOTE(asomya): This only works on ESXi if the port binding is
|
||||
# set to ephemeral
|
||||
if props.name == network_name:
|
||||
network_obj['type'] = 'DistributedVirtualPortgroup'
|
||||
network_obj['dvpg'] = props.key
|
||||
network_obj['dvsw'] = props.distributedVirtualSwitch.value
|
||||
else:
|
||||
props = session._call_method(vim_util,
|
||||
"get_dynamic_property", network,
|
||||
"Network", "summary.name")
|
||||
if props == network_name:
|
||||
network_obj['type'] = 'Network'
|
||||
network_obj['name'] = network_name
|
||||
if (len(network_obj) > 0):
|
||||
return network_obj
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_vswitch_for_vlan_interface(session, vlan_interface):
|
||||
"""
|
||||
Gets the vswitch associated with the physical network adapter
|
||||
with the name supplied.
|
||||
"""
|
||||
# Get the list of vSwicthes on the Host System
|
||||
host_mor = session._call_method(vim_util, "get_objects",
|
||||
"HostSystem")[0].obj
|
||||
vswitches_ret = session._call_method(vim_util,
|
||||
"get_dynamic_property", host_mor,
|
||||
"HostSystem", "config.network.vswitch")
|
||||
# Meaning there are no vSwitches on the host. Shouldn't be the case,
|
||||
# but just doing code check
|
||||
if not vswitches_ret:
|
||||
return
|
||||
vswitches = vswitches_ret.HostVirtualSwitch
|
||||
# Get the vSwitch associated with the network adapter
|
||||
for elem in vswitches:
|
||||
try:
|
||||
for nic_elem in elem.pnic:
|
||||
if str(nic_elem).split('-')[-1].find(vlan_interface) != -1:
|
||||
return elem.name
|
||||
# Catching Attribute error as a vSwitch may not be associated with a
|
||||
# physical NIC.
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def check_if_vlan_interface_exists(session, vlan_interface):
|
||||
"""Checks if the vlan_inteface exists on the esx host."""
|
||||
host_net_system_mor = session._call_method(vim_util, "get_objects",
|
||||
"HostSystem", ["configManager.networkSystem"])[0].propSet[0].val
|
||||
physical_nics_ret = session._call_method(vim_util,
|
||||
"get_dynamic_property", host_net_system_mor,
|
||||
"HostNetworkSystem", "networkInfo.pnic")
|
||||
# Meaning there are no physical nics on the host
|
||||
if not physical_nics_ret:
|
||||
return False
|
||||
physical_nics = physical_nics_ret.PhysicalNic
|
||||
for pnic in physical_nics:
|
||||
if vlan_interface == pnic.device:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_vlanid_and_vswitch_for_portgroup(session, pg_name):
|
||||
"""Get the vlan id and vswicth associated with the port group."""
|
||||
host_mor = session._call_method(vim_util, "get_objects",
|
||||
"HostSystem")[0].obj
|
||||
port_grps_on_host_ret = session._call_method(vim_util,
|
||||
"get_dynamic_property", host_mor,
|
||||
"HostSystem", "config.network.portgroup")
|
||||
if not port_grps_on_host_ret:
|
||||
excep = ("ESX SOAP server returned an empty port group "
|
||||
"for the host system in its response")
|
||||
LOG.exception(excep)
|
||||
raise exception.Error(_(excep))
|
||||
port_grps_on_host = port_grps_on_host_ret.HostPortGroup
|
||||
for p_gp in port_grps_on_host:
|
||||
if p_gp.spec.name == pg_name:
|
||||
p_grp_vswitch_name = p_gp.vswitch.split("-")[-1]
|
||||
return p_gp.spec.vlanId, p_grp_vswitch_name
|
||||
|
||||
|
||||
def create_port_group(session, pg_name, vswitch_name, vlan_id=0):
|
||||
"""
|
||||
Creates a port group on the host system with the vlan tags
|
||||
supplied. VLAN id 0 means no vlan id association.
|
||||
"""
|
||||
client_factory = session._get_vim().client.factory
|
||||
add_prt_grp_spec = vm_util.get_add_vswitch_port_group_spec(
|
||||
client_factory,
|
||||
vswitch_name,
|
||||
pg_name,
|
||||
vlan_id)
|
||||
host_mor = session._call_method(vim_util, "get_objects",
|
||||
"HostSystem")[0].obj
|
||||
network_system_mor = session._call_method(vim_util,
|
||||
"get_dynamic_property", host_mor,
|
||||
"HostSystem", "configManager.networkSystem")
|
||||
LOG.debug(_("Creating Port Group with name %s on "
|
||||
"the ESX host") % pg_name)
|
||||
try:
|
||||
session._call_method(session._get_vim(),
|
||||
"AddPortGroup", network_system_mor,
|
||||
portgrp=add_prt_grp_spec)
|
||||
except error_util.VimFaultException, exc:
|
||||
# There can be a race condition when two instances try
|
||||
# adding port groups at the same time. One succeeds, then
|
||||
# the other one will get an exception. Since we are
|
||||
# concerned with the port group being created, which is done
|
||||
# by the other call, we can ignore the exception.
|
||||
if error_util.FAULT_ALREADY_EXISTS not in exc.fault_list:
|
||||
raise exception.Error(exc)
|
||||
LOG.debug(_("Created Port Group with name %s on "
|
||||
"the ESX host") % pg_name)
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Utility functions for ESX Networking.
|
||||
"""
|
||||
|
||||
from nova import exception
|
||||
from nova import log as logging
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import vim_util
|
||||
from nova.virt.vmwareapi import vm_util
|
||||
|
||||
LOG = logging.getLogger("nova.virt.vmwareapi.network_utils")
|
||||
|
||||
|
||||
def get_network_with_the_name(session, network_name="vmnet0"):
|
||||
"""
|
||||
Gets reference to the network whose name is passed as the
|
||||
argument.
|
||||
"""
|
||||
hostsystems = session._call_method(vim_util, "get_objects",
|
||||
"HostSystem", ["network"])
|
||||
vm_networks_ret = hostsystems[0].propSet[0].val
|
||||
# Meaning there are no networks on the host. suds responds with a ""
|
||||
# in the parent property field rather than a [] in the
|
||||
# ManagedObjectRefernce property field of the parent
|
||||
if not vm_networks_ret:
|
||||
return None
|
||||
vm_networks = vm_networks_ret.ManagedObjectReference
|
||||
networks = session._call_method(vim_util,
|
||||
"get_properties_for_a_collection_of_objects",
|
||||
"Network", vm_networks, ["summary.name"])
|
||||
network_obj = {}
|
||||
LOG.warn(vm_networks)
|
||||
for network in vm_networks:
|
||||
# Get network properties
|
||||
if network._type == 'DistributedVirtualPortgroup':
|
||||
props = session._call_method(vim_util,
|
||||
"get_dynamic_property", network,
|
||||
"DistributedVirtualPortgroup", "config")
|
||||
# NOTE(asomya): This only works on ESXi if the port binding is
|
||||
# set to ephemeral
|
||||
if props.name == network_name:
|
||||
network_obj['type'] = 'DistributedVirtualPortgroup'
|
||||
network_obj['dvpg'] = props.key
|
||||
network_obj['dvsw'] = props.distributedVirtualSwitch.value
|
||||
else:
|
||||
props = session._call_method(vim_util,
|
||||
"get_dynamic_property", network,
|
||||
"Network", "summary.name")
|
||||
if props == network_name:
|
||||
network_obj['type'] = 'Network'
|
||||
network_obj['name'] = network_name
|
||||
if (len(network_obj) > 0):
|
||||
return network_obj
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_vswitch_for_vlan_interface(session, vlan_interface):
|
||||
"""
|
||||
Gets the vswitch associated with the physical network adapter
|
||||
with the name supplied.
|
||||
"""
|
||||
# Get the list of vSwicthes on the Host System
|
||||
host_mor = session._call_method(vim_util, "get_objects",
|
||||
"HostSystem")[0].obj
|
||||
vswitches_ret = session._call_method(vim_util,
|
||||
"get_dynamic_property", host_mor,
|
||||
"HostSystem", "config.network.vswitch")
|
||||
# Meaning there are no vSwitches on the host. Shouldn't be the case,
|
||||
# but just doing code check
|
||||
if not vswitches_ret:
|
||||
return
|
||||
vswitches = vswitches_ret.HostVirtualSwitch
|
||||
# Get the vSwitch associated with the network adapter
|
||||
for elem in vswitches:
|
||||
try:
|
||||
for nic_elem in elem.pnic:
|
||||
if str(nic_elem).split('-')[-1].find(vlan_interface) != -1:
|
||||
return elem.name
|
||||
# Catching Attribute error as a vSwitch may not be associated with a
|
||||
# physical NIC.
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def check_if_vlan_interface_exists(session, vlan_interface):
|
||||
"""Checks if the vlan_inteface exists on the esx host."""
|
||||
host_net_system_mor = session._call_method(vim_util, "get_objects",
|
||||
"HostSystem", ["configManager.networkSystem"])[0].propSet[0].val
|
||||
physical_nics_ret = session._call_method(vim_util,
|
||||
"get_dynamic_property", host_net_system_mor,
|
||||
"HostNetworkSystem", "networkInfo.pnic")
|
||||
# Meaning there are no physical nics on the host
|
||||
if not physical_nics_ret:
|
||||
return False
|
||||
physical_nics = physical_nics_ret.PhysicalNic
|
||||
for pnic in physical_nics:
|
||||
if vlan_interface == pnic.device:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_vlanid_and_vswitch_for_portgroup(session, pg_name):
|
||||
"""Get the vlan id and vswicth associated with the port group."""
|
||||
host_mor = session._call_method(vim_util, "get_objects",
|
||||
"HostSystem")[0].obj
|
||||
port_grps_on_host_ret = session._call_method(vim_util,
|
||||
"get_dynamic_property", host_mor,
|
||||
"HostSystem", "config.network.portgroup")
|
||||
if not port_grps_on_host_ret:
|
||||
excep = ("ESX SOAP server returned an empty port group "
|
||||
"for the host system in its response")
|
||||
LOG.exception(excep)
|
||||
raise exception.Error(_(excep))
|
||||
port_grps_on_host = port_grps_on_host_ret.HostPortGroup
|
||||
for p_gp in port_grps_on_host:
|
||||
if p_gp.spec.name == pg_name:
|
||||
p_grp_vswitch_name = p_gp.vswitch.split("-")[-1]
|
||||
return p_gp.spec.vlanId, p_grp_vswitch_name
|
||||
|
||||
|
||||
def create_port_group(session, pg_name, vswitch_name, vlan_id=0):
|
||||
"""
|
||||
Creates a port group on the host system with the vlan tags
|
||||
supplied. VLAN id 0 means no vlan id association.
|
||||
"""
|
||||
client_factory = session._get_vim().client.factory
|
||||
add_prt_grp_spec = vm_util.get_add_vswitch_port_group_spec(
|
||||
client_factory,
|
||||
vswitch_name,
|
||||
pg_name,
|
||||
vlan_id)
|
||||
host_mor = session._call_method(vim_util, "get_objects",
|
||||
"HostSystem")[0].obj
|
||||
network_system_mor = session._call_method(vim_util,
|
||||
"get_dynamic_property", host_mor,
|
||||
"HostSystem", "configManager.networkSystem")
|
||||
LOG.debug(_("Creating Port Group with name %s on "
|
||||
"the ESX host") % pg_name)
|
||||
try:
|
||||
session._call_method(session._get_vim(),
|
||||
"AddPortGroup", network_system_mor,
|
||||
portgrp=add_prt_grp_spec)
|
||||
except error_util.VimFaultException, exc:
|
||||
# There can be a race condition when two instances try
|
||||
# adding port groups at the same time. One succeeds, then
|
||||
# the other one will get an exception. Since we are
|
||||
# concerned with the port group being created, which is done
|
||||
# by the other call, we can ignore the exception.
|
||||
if error_util.FAULT_ALREADY_EXISTS not in exc.fault_list:
|
||||
raise exception.Error(exc)
|
||||
LOG.debug(_("Created Port Group with name %s on "
|
||||
"the ESX host") % pg_name)
|
||||
|
@ -1,179 +1,179 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Classes to handle image files
|
||||
|
||||
Collection of classes to handle image upload/download to/from Image service
|
||||
(like Glance image storage and retrieval service) from/to ESX/ESXi server.
|
||||
|
||||
"""
|
||||
|
||||
import httplib
|
||||
import urllib
|
||||
import urllib2
|
||||
import urlparse
|
||||
|
||||
from glance import client
|
||||
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
|
||||
LOG = logging.getLogger("nova.virt.vmwareapi.read_write_util")
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
USER_AGENT = "OpenStack-ESX-Adapter"
|
||||
|
||||
try:
|
||||
READ_CHUNKSIZE = client.BaseClient.CHUNKSIZE
|
||||
except AttributeError:
|
||||
READ_CHUNKSIZE = 65536
|
||||
|
||||
|
||||
class GlanceFileRead(object):
|
||||
"""Glance file read handler class."""
|
||||
|
||||
def __init__(self, glance_read_iter):
|
||||
self.glance_read_iter = glance_read_iter
|
||||
self.iter = self.get_next()
|
||||
|
||||
def read(self, chunk_size):
|
||||
"""Read an item from the queue. The chunk size is ignored for the
|
||||
Client ImageBodyIterator uses its own CHUNKSIZE."""
|
||||
try:
|
||||
return self.iter.next()
|
||||
except StopIteration:
|
||||
return ""
|
||||
|
||||
def get_next(self):
|
||||
"""Get the next item from the image iterator."""
|
||||
for data in self.glance_read_iter:
|
||||
yield data
|
||||
|
||||
def close(self):
|
||||
"""A dummy close just to maintain consistency."""
|
||||
pass
|
||||
|
||||
|
||||
class VMwareHTTPFile(object):
|
||||
"""Base class for HTTP file."""
|
||||
|
||||
def __init__(self, file_handle):
|
||||
self.eof = False
|
||||
self.file_handle = file_handle
|
||||
|
||||
def set_eof(self, eof):
|
||||
"""Set the end of file marker."""
|
||||
self.eof = eof
|
||||
|
||||
def get_eof(self):
|
||||
"""Check if the end of file has been reached."""
|
||||
return self.eof
|
||||
|
||||
def close(self):
|
||||
"""Close the file handle."""
|
||||
try:
|
||||
self.file_handle.close()
|
||||
except Exception, exc:
|
||||
LOG.exception(exc)
|
||||
|
||||
def __del__(self):
|
||||
"""Close the file handle on garbage collection."""
|
||||
self.close()
|
||||
|
||||
def _build_vim_cookie_headers(self, vim_cookies):
|
||||
"""Build ESX host session cookie headers."""
|
||||
cookie_header = ""
|
||||
for vim_cookie in vim_cookies:
|
||||
cookie_header = vim_cookie.name + "=" + vim_cookie.value
|
||||
break
|
||||
return cookie_header
|
||||
|
||||
def write(self, data):
|
||||
"""Write data to the file."""
|
||||
raise NotImplementedError
|
||||
|
||||
def read(self, chunk_size):
|
||||
"""Read a chunk of data."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_size(self):
|
||||
"""Get size of the file to be read."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class VMWareHTTPWriteFile(VMwareHTTPFile):
|
||||
"""VMWare file write handler class."""
|
||||
|
||||
def __init__(self, host, data_center_name, datastore_name, cookies,
|
||||
file_path, file_size, scheme="https"):
|
||||
base_url = "%s://%s/folder/%s" % (scheme, host, file_path)
|
||||
param_list = {"dcPath": data_center_name, "dsName": datastore_name}
|
||||
base_url = base_url + "?" + urllib.urlencode(param_list)
|
||||
(scheme, netloc, path, params, query, fragment) = \
|
||||
urlparse.urlparse(base_url)
|
||||
if scheme == "http":
|
||||
conn = httplib.HTTPConnection(netloc)
|
||||
elif scheme == "https":
|
||||
conn = httplib.HTTPSConnection(netloc)
|
||||
conn.putrequest("PUT", path + "?" + query)
|
||||
conn.putheader("User-Agent", USER_AGENT)
|
||||
conn.putheader("Content-Length", file_size)
|
||||
conn.putheader("Cookie", self._build_vim_cookie_headers(cookies))
|
||||
conn.endheaders()
|
||||
self.conn = conn
|
||||
VMwareHTTPFile.__init__(self, conn)
|
||||
|
||||
def write(self, data):
|
||||
"""Write to the file."""
|
||||
self.file_handle.send(data)
|
||||
|
||||
def close(self):
|
||||
"""Get the response and close the connection."""
|
||||
try:
|
||||
self.conn.getresponse()
|
||||
except Exception, excep:
|
||||
LOG.debug(_("Exception during HTTP connection close in "
|
||||
"VMWareHTTpWrite. Exception is %s") % excep)
|
||||
super(VMWareHTTPWriteFile, self).close()
|
||||
|
||||
|
||||
class VmWareHTTPReadFile(VMwareHTTPFile):
|
||||
"""VMWare file read handler class."""
|
||||
|
||||
def __init__(self, host, data_center_name, datastore_name, cookies,
|
||||
file_path, scheme="https"):
|
||||
base_url = "%s://%s/folder/%s" % (scheme, host,
|
||||
urllib.pathname2url(file_path))
|
||||
param_list = {"dcPath": data_center_name, "dsName": datastore_name}
|
||||
base_url = base_url + "?" + urllib.urlencode(param_list)
|
||||
headers = {'User-Agent': USER_AGENT,
|
||||
'Cookie': self._build_vim_cookie_headers(cookies)}
|
||||
request = urllib2.Request(base_url, None, headers)
|
||||
conn = urllib2.urlopen(request)
|
||||
VMwareHTTPFile.__init__(self, conn)
|
||||
|
||||
def read(self, chunk_size):
|
||||
"""Read a chunk of data."""
|
||||
# We are ignoring the chunk size passed for we want the pipe to hold
|
||||
# data items of the chunk-size that Glance Client uses for read
|
||||
# while writing.
|
||||
return self.file_handle.read(READ_CHUNKSIZE)
|
||||
|
||||
def get_size(self):
|
||||
"""Get size of the file to be read."""
|
||||
return self.file_handle.headers.get("Content-Length", -1)
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Classes to handle image files
|
||||
|
||||
Collection of classes to handle image upload/download to/from Image service
|
||||
(like Glance image storage and retrieval service) from/to ESX/ESXi server.
|
||||
|
||||
"""
|
||||
|
||||
import httplib
|
||||
import urllib
|
||||
import urllib2
|
||||
import urlparse
|
||||
|
||||
from glance import client
|
||||
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
|
||||
LOG = logging.getLogger("nova.virt.vmwareapi.read_write_util")
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
USER_AGENT = "OpenStack-ESX-Adapter"
|
||||
|
||||
try:
|
||||
READ_CHUNKSIZE = client.BaseClient.CHUNKSIZE
|
||||
except AttributeError:
|
||||
READ_CHUNKSIZE = 65536
|
||||
|
||||
|
||||
class GlanceFileRead(object):
|
||||
"""Glance file read handler class."""
|
||||
|
||||
def __init__(self, glance_read_iter):
|
||||
self.glance_read_iter = glance_read_iter
|
||||
self.iter = self.get_next()
|
||||
|
||||
def read(self, chunk_size):
|
||||
"""Read an item from the queue. The chunk size is ignored for the
|
||||
Client ImageBodyIterator uses its own CHUNKSIZE."""
|
||||
try:
|
||||
return self.iter.next()
|
||||
except StopIteration:
|
||||
return ""
|
||||
|
||||
def get_next(self):
|
||||
"""Get the next item from the image iterator."""
|
||||
for data in self.glance_read_iter:
|
||||
yield data
|
||||
|
||||
def close(self):
|
||||
"""A dummy close just to maintain consistency."""
|
||||
pass
|
||||
|
||||
|
||||
class VMwareHTTPFile(object):
|
||||
"""Base class for HTTP file."""
|
||||
|
||||
def __init__(self, file_handle):
|
||||
self.eof = False
|
||||
self.file_handle = file_handle
|
||||
|
||||
def set_eof(self, eof):
|
||||
"""Set the end of file marker."""
|
||||
self.eof = eof
|
||||
|
||||
def get_eof(self):
|
||||
"""Check if the end of file has been reached."""
|
||||
return self.eof
|
||||
|
||||
def close(self):
|
||||
"""Close the file handle."""
|
||||
try:
|
||||
self.file_handle.close()
|
||||
except Exception, exc:
|
||||
LOG.exception(exc)
|
||||
|
||||
def __del__(self):
|
||||
"""Close the file handle on garbage collection."""
|
||||
self.close()
|
||||
|
||||
def _build_vim_cookie_headers(self, vim_cookies):
|
||||
"""Build ESX host session cookie headers."""
|
||||
cookie_header = ""
|
||||
for vim_cookie in vim_cookies:
|
||||
cookie_header = vim_cookie.name + "=" + vim_cookie.value
|
||||
break
|
||||
return cookie_header
|
||||
|
||||
def write(self, data):
|
||||
"""Write data to the file."""
|
||||
raise NotImplementedError
|
||||
|
||||
def read(self, chunk_size):
|
||||
"""Read a chunk of data."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_size(self):
|
||||
"""Get size of the file to be read."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class VMWareHTTPWriteFile(VMwareHTTPFile):
|
||||
"""VMWare file write handler class."""
|
||||
|
||||
def __init__(self, host, data_center_name, datastore_name, cookies,
|
||||
file_path, file_size, scheme="https"):
|
||||
base_url = "%s://%s/folder/%s" % (scheme, host, file_path)
|
||||
param_list = {"dcPath": data_center_name, "dsName": datastore_name}
|
||||
base_url = base_url + "?" + urllib.urlencode(param_list)
|
||||
(scheme, netloc, path, params, query, fragment) = \
|
||||
urlparse.urlparse(base_url)
|
||||
if scheme == "http":
|
||||
conn = httplib.HTTPConnection(netloc)
|
||||
elif scheme == "https":
|
||||
conn = httplib.HTTPSConnection(netloc)
|
||||
conn.putrequest("PUT", path + "?" + query)
|
||||
conn.putheader("User-Agent", USER_AGENT)
|
||||
conn.putheader("Content-Length", file_size)
|
||||
conn.putheader("Cookie", self._build_vim_cookie_headers(cookies))
|
||||
conn.endheaders()
|
||||
self.conn = conn
|
||||
VMwareHTTPFile.__init__(self, conn)
|
||||
|
||||
def write(self, data):
|
||||
"""Write to the file."""
|
||||
self.file_handle.send(data)
|
||||
|
||||
def close(self):
|
||||
"""Get the response and close the connection."""
|
||||
try:
|
||||
self.conn.getresponse()
|
||||
except Exception, excep:
|
||||
LOG.debug(_("Exception during HTTP connection close in "
|
||||
"VMWareHTTpWrite. Exception is %s") % excep)
|
||||
super(VMWareHTTPWriteFile, self).close()
|
||||
|
||||
|
||||
class VmWareHTTPReadFile(VMwareHTTPFile):
|
||||
"""VMWare file read handler class."""
|
||||
|
||||
def __init__(self, host, data_center_name, datastore_name, cookies,
|
||||
file_path, scheme="https"):
|
||||
base_url = "%s://%s/folder/%s" % (scheme, host,
|
||||
urllib.pathname2url(file_path))
|
||||
param_list = {"dcPath": data_center_name, "dsName": datastore_name}
|
||||
base_url = base_url + "?" + urllib.urlencode(param_list)
|
||||
headers = {'User-Agent': USER_AGENT,
|
||||
'Cookie': self._build_vim_cookie_headers(cookies)}
|
||||
request = urllib2.Request(base_url, None, headers)
|
||||
conn = urllib2.urlopen(request)
|
||||
VMwareHTTPFile.__init__(self, conn)
|
||||
|
||||
def read(self, chunk_size):
|
||||
"""Read a chunk of data."""
|
||||
# We are ignoring the chunk size passed for we want the pipe to hold
|
||||
# data items of the chunk-size that Glance Client uses for read
|
||||
# while writing.
|
||||
return self.file_handle.read(READ_CHUNKSIZE)
|
||||
|
||||
def get_size(self):
|
||||
"""Get size of the file to be read."""
|
||||
return self.file_handle.headers.get("Content-Length", -1)
|
||||
|
@ -1,181 +1,181 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Classes for making VMware VI SOAP calls.
|
||||
"""
|
||||
|
||||
import httplib
|
||||
|
||||
try:
|
||||
import suds
|
||||
except ImportError:
|
||||
suds = None
|
||||
|
||||
from nova import flags
|
||||
from nova.virt.vmwareapi import error_util
|
||||
|
||||
RESP_NOT_XML_ERROR = 'Response is "text/html", not "text/xml"'
|
||||
CONN_ABORT_ERROR = 'Software caused connection abort'
|
||||
ADDRESS_IN_USE_ERROR = 'Address already in use'
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('vmwareapi_wsdl_loc',
|
||||
None,
|
||||
'VIM Service WSDL Location'
|
||||
'e.g http://<server>/vimService.wsdl'
|
||||
'Due to a bug in vSphere ESX 4.1 default wsdl'
|
||||
'Refer readme-vmware to setup')
|
||||
|
||||
|
||||
if suds:
|
||||
|
||||
class VIMMessagePlugin(suds.plugin.MessagePlugin):
|
||||
|
||||
def addAttributeForValue(self, node):
|
||||
# suds does not handle AnyType properly.
|
||||
# VI SDK requires type attribute to be set when AnyType is used
|
||||
if node.name == 'value':
|
||||
node.set('xsi:type', 'xsd:string')
|
||||
|
||||
def marshalled(self, context):
|
||||
"""suds will send the specified soap envelope.
|
||||
Provides the plugin with the opportunity to prune empty
|
||||
nodes and fixup nodes before sending it to the server.
|
||||
"""
|
||||
# suds builds the entire request object based on the wsdl schema.
|
||||
# VI SDK throws server errors if optional SOAP nodes are sent
|
||||
# without values, e.g. <test/> as opposed to <test>test</test>
|
||||
context.envelope.prune()
|
||||
context.envelope.walk(self.addAttributeForValue)
|
||||
|
||||
|
||||
class Vim:
|
||||
"""The VIM Object."""
|
||||
|
||||
def __init__(self,
|
||||
protocol="https",
|
||||
host="localhost"):
|
||||
"""
|
||||
Creates the necessary Communication interfaces and gets the
|
||||
ServiceContent for initiating SOAP transactions.
|
||||
|
||||
protocol: http or https
|
||||
host : ESX IPAddress[:port] or ESX Hostname[:port]
|
||||
"""
|
||||
if not suds:
|
||||
raise Exception(_("Unable to import suds."))
|
||||
|
||||
self._protocol = protocol
|
||||
self._host_name = host
|
||||
wsdl_url = FLAGS.vmwareapi_wsdl_loc
|
||||
if wsdl_url is None:
|
||||
raise Exception(_("Must specify vmwareapi_wsdl_loc"))
|
||||
# TODO(sateesh): Use this when VMware fixes their faulty wsdl
|
||||
#wsdl_url = '%s://%s/sdk/vimService.wsdl' % (self._protocol,
|
||||
# self._host_name)
|
||||
url = '%s://%s/sdk' % (self._protocol, self._host_name)
|
||||
self.client = suds.client.Client(wsdl_url, location=url,
|
||||
plugins=[VIMMessagePlugin()])
|
||||
self._service_content = \
|
||||
self.RetrieveServiceContent("ServiceInstance")
|
||||
|
||||
def get_service_content(self):
|
||||
"""Gets the service content object."""
|
||||
return self._service_content
|
||||
|
||||
def __getattr__(self, attr_name):
|
||||
"""Makes the API calls and gets the result."""
|
||||
try:
|
||||
return object.__getattr__(self, attr_name)
|
||||
except AttributeError:
|
||||
|
||||
def vim_request_handler(managed_object, **kwargs):
|
||||
"""
|
||||
Builds the SOAP message and parses the response for fault
|
||||
checking and other errors.
|
||||
|
||||
managed_object : Managed Object Reference or Managed
|
||||
Object Name
|
||||
**kwargs : Keyword arguments of the call
|
||||
"""
|
||||
# Dynamic handler for VI SDK Calls
|
||||
try:
|
||||
request_mo = \
|
||||
self._request_managed_object_builder(managed_object)
|
||||
request = getattr(self.client.service, attr_name)
|
||||
response = request(request_mo, **kwargs)
|
||||
# To check for the faults that are part of the message body
|
||||
# and not returned as Fault object response from the ESX
|
||||
# SOAP server
|
||||
if hasattr(error_util.FaultCheckers,
|
||||
attr_name.lower() + "_fault_checker"):
|
||||
fault_checker = getattr(error_util.FaultCheckers,
|
||||
attr_name.lower() + "_fault_checker")
|
||||
fault_checker(response)
|
||||
return response
|
||||
# Catch the VimFaultException that is raised by the fault
|
||||
# check of the SOAP response
|
||||
except error_util.VimFaultException, excep:
|
||||
raise
|
||||
except suds.WebFault, excep:
|
||||
doc = excep.document
|
||||
detail = doc.childAtPath("/Envelope/Body/Fault/detail")
|
||||
fault_list = []
|
||||
for child in detail.getChildren():
|
||||
fault_list.append(child.get("type"))
|
||||
raise error_util.VimFaultException(fault_list, excep)
|
||||
except AttributeError, excep:
|
||||
raise error_util.VimAttributeError(_("No such SOAP method "
|
||||
"'%s' provided by VI SDK") % (attr_name), excep)
|
||||
except (httplib.CannotSendRequest,
|
||||
httplib.ResponseNotReady,
|
||||
httplib.CannotSendHeader), excep:
|
||||
raise error_util.SessionOverLoadException(_("httplib "
|
||||
"error in %s: ") % (attr_name), excep)
|
||||
except Exception, excep:
|
||||
# Socket errors which need special handling for they
|
||||
# might be caused by ESX API call overload
|
||||
if (str(excep).find(ADDRESS_IN_USE_ERROR) != -1 or
|
||||
str(excep).find(CONN_ABORT_ERROR)) != -1:
|
||||
raise error_util.SessionOverLoadException(_("Socket "
|
||||
"error in %s: ") % (attr_name), excep)
|
||||
# Type error that needs special handling for it might be
|
||||
# caused by ESX host API call overload
|
||||
elif str(excep).find(RESP_NOT_XML_ERROR) != -1:
|
||||
raise error_util.SessionOverLoadException(_("Type "
|
||||
"error in %s: ") % (attr_name), excep)
|
||||
else:
|
||||
raise error_util.VimException(
|
||||
_("Exception in %s ") % (attr_name), excep)
|
||||
return vim_request_handler
|
||||
|
||||
def _request_managed_object_builder(self, managed_object):
|
||||
"""Builds the request managed object."""
|
||||
# Request Managed Object Builder
|
||||
if isinstance(managed_object, str):
|
||||
mo = suds.sudsobject.Property(managed_object)
|
||||
mo._type = managed_object
|
||||
else:
|
||||
mo = managed_object
|
||||
return mo
|
||||
|
||||
def __repr__(self):
|
||||
return "VIM Object"
|
||||
|
||||
def __str__(self):
|
||||
return "VIM Object"
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Classes for making VMware VI SOAP calls.
|
||||
"""
|
||||
|
||||
import httplib
|
||||
|
||||
try:
|
||||
import suds
|
||||
except ImportError:
|
||||
suds = None
|
||||
|
||||
from nova import flags
|
||||
from nova.virt.vmwareapi import error_util
|
||||
|
||||
RESP_NOT_XML_ERROR = 'Response is "text/html", not "text/xml"'
|
||||
CONN_ABORT_ERROR = 'Software caused connection abort'
|
||||
ADDRESS_IN_USE_ERROR = 'Address already in use'
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('vmwareapi_wsdl_loc',
|
||||
None,
|
||||
'VIM Service WSDL Location'
|
||||
'e.g http://<server>/vimService.wsdl'
|
||||
'Due to a bug in vSphere ESX 4.1 default wsdl'
|
||||
'Refer readme-vmware to setup')
|
||||
|
||||
|
||||
if suds:
|
||||
|
||||
class VIMMessagePlugin(suds.plugin.MessagePlugin):
|
||||
|
||||
def addAttributeForValue(self, node):
|
||||
# suds does not handle AnyType properly.
|
||||
# VI SDK requires type attribute to be set when AnyType is used
|
||||
if node.name == 'value':
|
||||
node.set('xsi:type', 'xsd:string')
|
||||
|
||||
def marshalled(self, context):
|
||||
"""suds will send the specified soap envelope.
|
||||
Provides the plugin with the opportunity to prune empty
|
||||
nodes and fixup nodes before sending it to the server.
|
||||
"""
|
||||
# suds builds the entire request object based on the wsdl schema.
|
||||
# VI SDK throws server errors if optional SOAP nodes are sent
|
||||
# without values, e.g. <test/> as opposed to <test>test</test>
|
||||
context.envelope.prune()
|
||||
context.envelope.walk(self.addAttributeForValue)
|
||||
|
||||
|
||||
class Vim:
|
||||
"""The VIM Object."""
|
||||
|
||||
def __init__(self,
|
||||
protocol="https",
|
||||
host="localhost"):
|
||||
"""
|
||||
Creates the necessary Communication interfaces and gets the
|
||||
ServiceContent for initiating SOAP transactions.
|
||||
|
||||
protocol: http or https
|
||||
host : ESX IPAddress[:port] or ESX Hostname[:port]
|
||||
"""
|
||||
if not suds:
|
||||
raise Exception(_("Unable to import suds."))
|
||||
|
||||
self._protocol = protocol
|
||||
self._host_name = host
|
||||
wsdl_url = FLAGS.vmwareapi_wsdl_loc
|
||||
if wsdl_url is None:
|
||||
raise Exception(_("Must specify vmwareapi_wsdl_loc"))
|
||||
# TODO(sateesh): Use this when VMware fixes their faulty wsdl
|
||||
#wsdl_url = '%s://%s/sdk/vimService.wsdl' % (self._protocol,
|
||||
# self._host_name)
|
||||
url = '%s://%s/sdk' % (self._protocol, self._host_name)
|
||||
self.client = suds.client.Client(wsdl_url, location=url,
|
||||
plugins=[VIMMessagePlugin()])
|
||||
self._service_content = \
|
||||
self.RetrieveServiceContent("ServiceInstance")
|
||||
|
||||
def get_service_content(self):
|
||||
"""Gets the service content object."""
|
||||
return self._service_content
|
||||
|
||||
def __getattr__(self, attr_name):
|
||||
"""Makes the API calls and gets the result."""
|
||||
try:
|
||||
return object.__getattr__(self, attr_name)
|
||||
except AttributeError:
|
||||
|
||||
def vim_request_handler(managed_object, **kwargs):
|
||||
"""
|
||||
Builds the SOAP message and parses the response for fault
|
||||
checking and other errors.
|
||||
|
||||
managed_object : Managed Object Reference or Managed
|
||||
Object Name
|
||||
**kwargs : Keyword arguments of the call
|
||||
"""
|
||||
# Dynamic handler for VI SDK Calls
|
||||
try:
|
||||
request_mo = \
|
||||
self._request_managed_object_builder(managed_object)
|
||||
request = getattr(self.client.service, attr_name)
|
||||
response = request(request_mo, **kwargs)
|
||||
# To check for the faults that are part of the message body
|
||||
# and not returned as Fault object response from the ESX
|
||||
# SOAP server
|
||||
if hasattr(error_util.FaultCheckers,
|
||||
attr_name.lower() + "_fault_checker"):
|
||||
fault_checker = getattr(error_util.FaultCheckers,
|
||||
attr_name.lower() + "_fault_checker")
|
||||
fault_checker(response)
|
||||
return response
|
||||
# Catch the VimFaultException that is raised by the fault
|
||||
# check of the SOAP response
|
||||
except error_util.VimFaultException, excep:
|
||||
raise
|
||||
except suds.WebFault, excep:
|
||||
doc = excep.document
|
||||
detail = doc.childAtPath("/Envelope/Body/Fault/detail")
|
||||
fault_list = []
|
||||
for child in detail.getChildren():
|
||||
fault_list.append(child.get("type"))
|
||||
raise error_util.VimFaultException(fault_list, excep)
|
||||
except AttributeError, excep:
|
||||
raise error_util.VimAttributeError(_("No such SOAP method "
|
||||
"'%s' provided by VI SDK") % (attr_name), excep)
|
||||
except (httplib.CannotSendRequest,
|
||||
httplib.ResponseNotReady,
|
||||
httplib.CannotSendHeader), excep:
|
||||
raise error_util.SessionOverLoadException(_("httplib "
|
||||
"error in %s: ") % (attr_name), excep)
|
||||
except Exception, excep:
|
||||
# Socket errors which need special handling for they
|
||||
# might be caused by ESX API call overload
|
||||
if (str(excep).find(ADDRESS_IN_USE_ERROR) != -1 or
|
||||
str(excep).find(CONN_ABORT_ERROR)) != -1:
|
||||
raise error_util.SessionOverLoadException(_("Socket "
|
||||
"error in %s: ") % (attr_name), excep)
|
||||
# Type error that needs special handling for it might be
|
||||
# caused by ESX host API call overload
|
||||
elif str(excep).find(RESP_NOT_XML_ERROR) != -1:
|
||||
raise error_util.SessionOverLoadException(_("Type "
|
||||
"error in %s: ") % (attr_name), excep)
|
||||
else:
|
||||
raise error_util.VimException(
|
||||
_("Exception in %s ") % (attr_name), excep)
|
||||
return vim_request_handler
|
||||
|
||||
def _request_managed_object_builder(self, managed_object):
|
||||
"""Builds the request managed object."""
|
||||
# Request Managed Object Builder
|
||||
if isinstance(managed_object, str):
|
||||
mo = suds.sudsobject.Property(managed_object)
|
||||
mo._type = managed_object
|
||||
else:
|
||||
mo = managed_object
|
||||
return mo
|
||||
|
||||
def __repr__(self):
|
||||
return "VIM Object"
|
||||
|
||||
def __str__(self):
|
||||
return "VIM Object"
|
||||
|
@ -1,223 +1,223 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
The VMware API utility module.
|
||||
"""
|
||||
|
||||
|
||||
def build_selection_spec(client_factory, name):
|
||||
"""Builds the selection spec."""
|
||||
sel_spec = client_factory.create('ns0:SelectionSpec')
|
||||
sel_spec.name = name
|
||||
return sel_spec
|
||||
|
||||
|
||||
def build_traversal_spec(client_factory, name, spec_type, path, skip,
|
||||
select_set):
|
||||
"""Builds the traversal spec object."""
|
||||
traversal_spec = client_factory.create('ns0:TraversalSpec')
|
||||
traversal_spec.name = name
|
||||
traversal_spec.type = spec_type
|
||||
traversal_spec.path = path
|
||||
traversal_spec.skip = skip
|
||||
traversal_spec.selectSet = select_set
|
||||
return traversal_spec
|
||||
|
||||
|
||||
def build_recursive_traversal_spec(client_factory):
|
||||
"""
|
||||
Builds the Recursive Traversal Spec to traverse the object managed
|
||||
object hierarchy.
|
||||
"""
|
||||
visit_folders_select_spec = build_selection_spec(client_factory,
|
||||
"visitFolders")
|
||||
# For getting to hostFolder from datacenter
|
||||
dc_to_hf = build_traversal_spec(client_factory, "dc_to_hf", "Datacenter",
|
||||
"hostFolder", False,
|
||||
[visit_folders_select_spec])
|
||||
# For getting to vmFolder from datacenter
|
||||
dc_to_vmf = build_traversal_spec(client_factory, "dc_to_vmf", "Datacenter",
|
||||
"vmFolder", False,
|
||||
[visit_folders_select_spec])
|
||||
# For getting Host System to virtual machine
|
||||
h_to_vm = build_traversal_spec(client_factory, "h_to_vm", "HostSystem",
|
||||
"vm", False,
|
||||
[visit_folders_select_spec])
|
||||
|
||||
# For getting to Host System from Compute Resource
|
||||
cr_to_h = build_traversal_spec(client_factory, "cr_to_h",
|
||||
"ComputeResource", "host", False, [])
|
||||
|
||||
# For getting to datastore from Compute Resource
|
||||
cr_to_ds = build_traversal_spec(client_factory, "cr_to_ds",
|
||||
"ComputeResource", "datastore", False, [])
|
||||
|
||||
rp_to_rp_select_spec = build_selection_spec(client_factory, "rp_to_rp")
|
||||
rp_to_vm_select_spec = build_selection_spec(client_factory, "rp_to_vm")
|
||||
# For getting to resource pool from Compute Resource
|
||||
cr_to_rp = build_traversal_spec(client_factory, "cr_to_rp",
|
||||
"ComputeResource", "resourcePool", False,
|
||||
[rp_to_rp_select_spec, rp_to_vm_select_spec])
|
||||
|
||||
# For getting to child res pool from the parent res pool
|
||||
rp_to_rp = build_traversal_spec(client_factory, "rp_to_rp", "ResourcePool",
|
||||
"resourcePool", False,
|
||||
[rp_to_rp_select_spec, rp_to_vm_select_spec])
|
||||
|
||||
# For getting to Virtual Machine from the Resource Pool
|
||||
rp_to_vm = build_traversal_spec(client_factory, "rp_to_vm", "ResourcePool",
|
||||
"vm", False,
|
||||
[rp_to_rp_select_spec, rp_to_vm_select_spec])
|
||||
|
||||
# Get the assorted traversal spec which takes care of the objects to
|
||||
# be searched for from the root folder
|
||||
traversal_spec = build_traversal_spec(client_factory, "visitFolders",
|
||||
"Folder", "childEntity", False,
|
||||
[visit_folders_select_spec, dc_to_hf,
|
||||
dc_to_vmf, cr_to_ds, cr_to_h, cr_to_rp,
|
||||
rp_to_rp, h_to_vm, rp_to_vm])
|
||||
return traversal_spec
|
||||
|
||||
|
||||
def build_property_spec(client_factory, type="VirtualMachine",
|
||||
properties_to_collect=None,
|
||||
all_properties=False):
|
||||
"""Builds the Property Spec."""
|
||||
if not properties_to_collect:
|
||||
properties_to_collect = ["name"]
|
||||
|
||||
property_spec = client_factory.create('ns0:PropertySpec')
|
||||
property_spec.all = all_properties
|
||||
property_spec.pathSet = properties_to_collect
|
||||
property_spec.type = type
|
||||
return property_spec
|
||||
|
||||
|
||||
def build_object_spec(client_factory, root_folder, traversal_specs):
|
||||
"""Builds the object Spec."""
|
||||
object_spec = client_factory.create('ns0:ObjectSpec')
|
||||
object_spec.obj = root_folder
|
||||
object_spec.skip = False
|
||||
object_spec.selectSet = traversal_specs
|
||||
return object_spec
|
||||
|
||||
|
||||
def build_property_filter_spec(client_factory, property_specs, object_specs):
|
||||
"""Builds the Property Filter Spec."""
|
||||
property_filter_spec = client_factory.create('ns0:PropertyFilterSpec')
|
||||
property_filter_spec.propSet = property_specs
|
||||
property_filter_spec.objectSet = object_specs
|
||||
return property_filter_spec
|
||||
|
||||
|
||||
def get_object_properties(vim, collector, mobj, type, properties):
|
||||
"""Gets the properties of the Managed object specified."""
|
||||
client_factory = vim.client.factory
|
||||
if mobj is None:
|
||||
return None
|
||||
usecoll = collector
|
||||
if usecoll is None:
|
||||
usecoll = vim.get_service_content().propertyCollector
|
||||
property_filter_spec = client_factory.create('ns0:PropertyFilterSpec')
|
||||
property_spec = client_factory.create('ns0:PropertySpec')
|
||||
property_spec.all = (properties is None or len(properties) == 0)
|
||||
property_spec.pathSet = properties
|
||||
property_spec.type = type
|
||||
object_spec = client_factory.create('ns0:ObjectSpec')
|
||||
object_spec.obj = mobj
|
||||
object_spec.skip = False
|
||||
property_filter_spec.propSet = [property_spec]
|
||||
property_filter_spec.objectSet = [object_spec]
|
||||
return vim.RetrieveProperties(usecoll, specSet=[property_filter_spec])
|
||||
|
||||
|
||||
def get_dynamic_property(vim, mobj, type, property_name):
|
||||
"""Gets a particular property of the Managed Object."""
|
||||
obj_content = \
|
||||
get_object_properties(vim, None, mobj, type, [property_name])
|
||||
property_value = None
|
||||
if obj_content:
|
||||
dynamic_property = obj_content[0].propSet
|
||||
if dynamic_property:
|
||||
property_value = dynamic_property[0].val
|
||||
return property_value
|
||||
|
||||
|
||||
def get_objects(vim, type, properties_to_collect=None, all=False):
|
||||
"""Gets the list of objects of the type specified."""
|
||||
if not properties_to_collect:
|
||||
properties_to_collect = ["name"]
|
||||
|
||||
client_factory = vim.client.factory
|
||||
object_spec = build_object_spec(client_factory,
|
||||
vim.get_service_content().rootFolder,
|
||||
[build_recursive_traversal_spec(client_factory)])
|
||||
property_spec = build_property_spec(client_factory, type=type,
|
||||
properties_to_collect=properties_to_collect,
|
||||
all_properties=all)
|
||||
property_filter_spec = build_property_filter_spec(client_factory,
|
||||
[property_spec],
|
||||
[object_spec])
|
||||
return vim.RetrieveProperties(vim.get_service_content().propertyCollector,
|
||||
specSet=[property_filter_spec])
|
||||
|
||||
|
||||
def get_prop_spec(client_factory, spec_type, properties):
|
||||
"""Builds the Property Spec Object."""
|
||||
prop_spec = client_factory.create('ns0:PropertySpec')
|
||||
prop_spec.type = spec_type
|
||||
prop_spec.pathSet = properties
|
||||
return prop_spec
|
||||
|
||||
|
||||
def get_obj_spec(client_factory, obj, select_set=None):
|
||||
"""Builds the Object Spec object."""
|
||||
obj_spec = client_factory.create('ns0:ObjectSpec')
|
||||
obj_spec.obj = obj
|
||||
obj_spec.skip = False
|
||||
if select_set is not None:
|
||||
obj_spec.selectSet = select_set
|
||||
return obj_spec
|
||||
|
||||
|
||||
def get_prop_filter_spec(client_factory, obj_spec, prop_spec):
|
||||
"""Builds the Property Filter Spec Object."""
|
||||
prop_filter_spec = \
|
||||
client_factory.create('ns0:PropertyFilterSpec')
|
||||
prop_filter_spec.propSet = prop_spec
|
||||
prop_filter_spec.objectSet = obj_spec
|
||||
return prop_filter_spec
|
||||
|
||||
|
||||
def get_properties_for_a_collection_of_objects(vim, type,
|
||||
obj_list, properties):
|
||||
"""
|
||||
Gets the list of properties for the collection of
|
||||
objects of the type specified.
|
||||
"""
|
||||
client_factory = vim.client.factory
|
||||
if len(obj_list) == 0:
|
||||
return []
|
||||
prop_spec = get_prop_spec(client_factory, type, properties)
|
||||
lst_obj_specs = []
|
||||
for obj in obj_list:
|
||||
lst_obj_specs.append(get_obj_spec(client_factory, obj))
|
||||
prop_filter_spec = get_prop_filter_spec(client_factory,
|
||||
lst_obj_specs, [prop_spec])
|
||||
return vim.RetrieveProperties(vim.get_service_content().propertyCollector,
|
||||
specSet=[prop_filter_spec])
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
The VMware API utility module.
|
||||
"""
|
||||
|
||||
|
||||
def build_selection_spec(client_factory, name):
|
||||
"""Builds the selection spec."""
|
||||
sel_spec = client_factory.create('ns0:SelectionSpec')
|
||||
sel_spec.name = name
|
||||
return sel_spec
|
||||
|
||||
|
||||
def build_traversal_spec(client_factory, name, spec_type, path, skip,
|
||||
select_set):
|
||||
"""Builds the traversal spec object."""
|
||||
traversal_spec = client_factory.create('ns0:TraversalSpec')
|
||||
traversal_spec.name = name
|
||||
traversal_spec.type = spec_type
|
||||
traversal_spec.path = path
|
||||
traversal_spec.skip = skip
|
||||
traversal_spec.selectSet = select_set
|
||||
return traversal_spec
|
||||
|
||||
|
||||
def build_recursive_traversal_spec(client_factory):
|
||||
"""
|
||||
Builds the Recursive Traversal Spec to traverse the object managed
|
||||
object hierarchy.
|
||||
"""
|
||||
visit_folders_select_spec = build_selection_spec(client_factory,
|
||||
"visitFolders")
|
||||
# For getting to hostFolder from datacenter
|
||||
dc_to_hf = build_traversal_spec(client_factory, "dc_to_hf", "Datacenter",
|
||||
"hostFolder", False,
|
||||
[visit_folders_select_spec])
|
||||
# For getting to vmFolder from datacenter
|
||||
dc_to_vmf = build_traversal_spec(client_factory, "dc_to_vmf", "Datacenter",
|
||||
"vmFolder", False,
|
||||
[visit_folders_select_spec])
|
||||
# For getting Host System to virtual machine
|
||||
h_to_vm = build_traversal_spec(client_factory, "h_to_vm", "HostSystem",
|
||||
"vm", False,
|
||||
[visit_folders_select_spec])
|
||||
|
||||
# For getting to Host System from Compute Resource
|
||||
cr_to_h = build_traversal_spec(client_factory, "cr_to_h",
|
||||
"ComputeResource", "host", False, [])
|
||||
|
||||
# For getting to datastore from Compute Resource
|
||||
cr_to_ds = build_traversal_spec(client_factory, "cr_to_ds",
|
||||
"ComputeResource", "datastore", False, [])
|
||||
|
||||
rp_to_rp_select_spec = build_selection_spec(client_factory, "rp_to_rp")
|
||||
rp_to_vm_select_spec = build_selection_spec(client_factory, "rp_to_vm")
|
||||
# For getting to resource pool from Compute Resource
|
||||
cr_to_rp = build_traversal_spec(client_factory, "cr_to_rp",
|
||||
"ComputeResource", "resourcePool", False,
|
||||
[rp_to_rp_select_spec, rp_to_vm_select_spec])
|
||||
|
||||
# For getting to child res pool from the parent res pool
|
||||
rp_to_rp = build_traversal_spec(client_factory, "rp_to_rp", "ResourcePool",
|
||||
"resourcePool", False,
|
||||
[rp_to_rp_select_spec, rp_to_vm_select_spec])
|
||||
|
||||
# For getting to Virtual Machine from the Resource Pool
|
||||
rp_to_vm = build_traversal_spec(client_factory, "rp_to_vm", "ResourcePool",
|
||||
"vm", False,
|
||||
[rp_to_rp_select_spec, rp_to_vm_select_spec])
|
||||
|
||||
# Get the assorted traversal spec which takes care of the objects to
|
||||
# be searched for from the root folder
|
||||
traversal_spec = build_traversal_spec(client_factory, "visitFolders",
|
||||
"Folder", "childEntity", False,
|
||||
[visit_folders_select_spec, dc_to_hf,
|
||||
dc_to_vmf, cr_to_ds, cr_to_h, cr_to_rp,
|
||||
rp_to_rp, h_to_vm, rp_to_vm])
|
||||
return traversal_spec
|
||||
|
||||
|
||||
def build_property_spec(client_factory, type="VirtualMachine",
|
||||
properties_to_collect=None,
|
||||
all_properties=False):
|
||||
"""Builds the Property Spec."""
|
||||
if not properties_to_collect:
|
||||
properties_to_collect = ["name"]
|
||||
|
||||
property_spec = client_factory.create('ns0:PropertySpec')
|
||||
property_spec.all = all_properties
|
||||
property_spec.pathSet = properties_to_collect
|
||||
property_spec.type = type
|
||||
return property_spec
|
||||
|
||||
|
||||
def build_object_spec(client_factory, root_folder, traversal_specs):
|
||||
"""Builds the object Spec."""
|
||||
object_spec = client_factory.create('ns0:ObjectSpec')
|
||||
object_spec.obj = root_folder
|
||||
object_spec.skip = False
|
||||
object_spec.selectSet = traversal_specs
|
||||
return object_spec
|
||||
|
||||
|
||||
def build_property_filter_spec(client_factory, property_specs, object_specs):
|
||||
"""Builds the Property Filter Spec."""
|
||||
property_filter_spec = client_factory.create('ns0:PropertyFilterSpec')
|
||||
property_filter_spec.propSet = property_specs
|
||||
property_filter_spec.objectSet = object_specs
|
||||
return property_filter_spec
|
||||
|
||||
|
||||
def get_object_properties(vim, collector, mobj, type, properties):
|
||||
"""Gets the properties of the Managed object specified."""
|
||||
client_factory = vim.client.factory
|
||||
if mobj is None:
|
||||
return None
|
||||
usecoll = collector
|
||||
if usecoll is None:
|
||||
usecoll = vim.get_service_content().propertyCollector
|
||||
property_filter_spec = client_factory.create('ns0:PropertyFilterSpec')
|
||||
property_spec = client_factory.create('ns0:PropertySpec')
|
||||
property_spec.all = (properties is None or len(properties) == 0)
|
||||
property_spec.pathSet = properties
|
||||
property_spec.type = type
|
||||
object_spec = client_factory.create('ns0:ObjectSpec')
|
||||
object_spec.obj = mobj
|
||||
object_spec.skip = False
|
||||
property_filter_spec.propSet = [property_spec]
|
||||
property_filter_spec.objectSet = [object_spec]
|
||||
return vim.RetrieveProperties(usecoll, specSet=[property_filter_spec])
|
||||
|
||||
|
||||
def get_dynamic_property(vim, mobj, type, property_name):
|
||||
"""Gets a particular property of the Managed Object."""
|
||||
obj_content = \
|
||||
get_object_properties(vim, None, mobj, type, [property_name])
|
||||
property_value = None
|
||||
if obj_content:
|
||||
dynamic_property = obj_content[0].propSet
|
||||
if dynamic_property:
|
||||
property_value = dynamic_property[0].val
|
||||
return property_value
|
||||
|
||||
|
||||
def get_objects(vim, type, properties_to_collect=None, all=False):
|
||||
"""Gets the list of objects of the type specified."""
|
||||
if not properties_to_collect:
|
||||
properties_to_collect = ["name"]
|
||||
|
||||
client_factory = vim.client.factory
|
||||
object_spec = build_object_spec(client_factory,
|
||||
vim.get_service_content().rootFolder,
|
||||
[build_recursive_traversal_spec(client_factory)])
|
||||
property_spec = build_property_spec(client_factory, type=type,
|
||||
properties_to_collect=properties_to_collect,
|
||||
all_properties=all)
|
||||
property_filter_spec = build_property_filter_spec(client_factory,
|
||||
[property_spec],
|
||||
[object_spec])
|
||||
return vim.RetrieveProperties(vim.get_service_content().propertyCollector,
|
||||
specSet=[property_filter_spec])
|
||||
|
||||
|
||||
def get_prop_spec(client_factory, spec_type, properties):
|
||||
"""Builds the Property Spec Object."""
|
||||
prop_spec = client_factory.create('ns0:PropertySpec')
|
||||
prop_spec.type = spec_type
|
||||
prop_spec.pathSet = properties
|
||||
return prop_spec
|
||||
|
||||
|
||||
def get_obj_spec(client_factory, obj, select_set=None):
|
||||
"""Builds the Object Spec object."""
|
||||
obj_spec = client_factory.create('ns0:ObjectSpec')
|
||||
obj_spec.obj = obj
|
||||
obj_spec.skip = False
|
||||
if select_set is not None:
|
||||
obj_spec.selectSet = select_set
|
||||
return obj_spec
|
||||
|
||||
|
||||
def get_prop_filter_spec(client_factory, obj_spec, prop_spec):
|
||||
"""Builds the Property Filter Spec Object."""
|
||||
prop_filter_spec = \
|
||||
client_factory.create('ns0:PropertyFilterSpec')
|
||||
prop_filter_spec.propSet = prop_spec
|
||||
prop_filter_spec.objectSet = obj_spec
|
||||
return prop_filter_spec
|
||||
|
||||
|
||||
def get_properties_for_a_collection_of_objects(vim, type,
|
||||
obj_list, properties):
|
||||
"""
|
||||
Gets the list of properties for the collection of
|
||||
objects of the type specified.
|
||||
"""
|
||||
client_factory = vim.client.factory
|
||||
if len(obj_list) == 0:
|
||||
return []
|
||||
prop_spec = get_prop_spec(client_factory, type, properties)
|
||||
lst_obj_specs = []
|
||||
for obj in obj_list:
|
||||
lst_obj_specs.append(get_obj_spec(client_factory, obj))
|
||||
prop_filter_spec = get_prop_filter_spec(client_factory,
|
||||
lst_obj_specs, [prop_spec])
|
||||
return vim.RetrieveProperties(vim.get_service_content().propertyCollector,
|
||||
specSet=[prop_filter_spec])
|
||||
|
@ -1,325 +1,325 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
The VMware API VM utility module to build SOAP object specs.
|
||||
"""
|
||||
|
||||
|
||||
def build_datastore_path(datastore_name, path):
|
||||
"""Build the datastore compliant path."""
|
||||
return "[%s] %s" % (datastore_name, path)
|
||||
|
||||
|
||||
def split_datastore_path(datastore_path):
|
||||
"""
|
||||
Split the VMWare style datastore path to get the Datastore
|
||||
name and the entity path.
|
||||
"""
|
||||
spl = datastore_path.split('[', 1)[1].split(']', 1)
|
||||
path = ""
|
||||
if len(spl) == 1:
|
||||
datastore_url = spl[0]
|
||||
else:
|
||||
datastore_url, path = spl
|
||||
return datastore_url, path.strip()
|
||||
|
||||
|
||||
def get_vm_create_spec(client_factory, instance, data_store_name,
|
||||
vif_infos, os_type="otherGuest"):
|
||||
"""Builds the VM Create spec."""
|
||||
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
config_spec.name = instance.name
|
||||
config_spec.guestId = os_type
|
||||
|
||||
vm_file_info = client_factory.create('ns0:VirtualMachineFileInfo')
|
||||
vm_file_info.vmPathName = "[" + data_store_name + "]"
|
||||
config_spec.files = vm_file_info
|
||||
|
||||
tools_info = client_factory.create('ns0:ToolsConfigInfo')
|
||||
tools_info.afterPowerOn = True
|
||||
tools_info.afterResume = True
|
||||
tools_info.beforeGuestStandby = True
|
||||
tools_info.beforeGuestShutdown = True
|
||||
tools_info.beforeGuestReboot = True
|
||||
|
||||
config_spec.tools = tools_info
|
||||
config_spec.numCPUs = int(instance.vcpus)
|
||||
config_spec.memoryMB = int(instance.memory_mb)
|
||||
|
||||
vif_spec_list = []
|
||||
for vif_info in vif_infos:
|
||||
vif_spec = create_network_spec(client_factory, vif_info)
|
||||
vif_spec_list.append(vif_spec)
|
||||
|
||||
device_config_spec = vif_spec_list
|
||||
|
||||
config_spec.deviceChange = device_config_spec
|
||||
return config_spec
|
||||
|
||||
|
||||
def create_controller_spec(client_factory, key):
|
||||
"""
|
||||
Builds a Config Spec for the LSI Logic Controller's addition
|
||||
which acts as the controller for the virtual hard disk to be attached
|
||||
to the VM.
|
||||
"""
|
||||
# Create a controller for the Virtual Hard Disk
|
||||
virtual_device_config = \
|
||||
client_factory.create('ns0:VirtualDeviceConfigSpec')
|
||||
virtual_device_config.operation = "add"
|
||||
virtual_lsi = \
|
||||
client_factory.create('ns0:VirtualLsiLogicController')
|
||||
virtual_lsi.key = key
|
||||
virtual_lsi.busNumber = 0
|
||||
virtual_lsi.sharedBus = "noSharing"
|
||||
virtual_device_config.device = virtual_lsi
|
||||
return virtual_device_config
|
||||
|
||||
|
||||
def create_network_spec(client_factory, vif_info):
|
||||
"""
|
||||
Builds a config spec for the addition of a new network
|
||||
adapter to the VM.
|
||||
"""
|
||||
network_spec = \
|
||||
client_factory.create('ns0:VirtualDeviceConfigSpec')
|
||||
network_spec.operation = "add"
|
||||
|
||||
# Get the recommended card type for the VM based on the guest OS of the VM
|
||||
net_device = client_factory.create('ns0:VirtualPCNet32')
|
||||
|
||||
# NOTE(asomya): Only works on ESXi if the portgroup binding is set to
|
||||
# ephemeral. Invalid configuration if set to static and the NIC does
|
||||
# not come up on boot if set to dynamic.
|
||||
network_ref = vif_info['network_ref']
|
||||
network_name = vif_info['network_name']
|
||||
mac_address = vif_info['mac_address']
|
||||
backing = None
|
||||
if (network_ref and
|
||||
network_ref['type'] == "DistributedVirtualPortgroup"):
|
||||
backing_name = \
|
||||
'ns0:VirtualEthernetCardDistributedVirtualPortBackingInfo'
|
||||
backing = \
|
||||
client_factory.create(backing_name)
|
||||
portgroup = \
|
||||
client_factory.create('ns0:DistributedVirtualSwitchPortConnection')
|
||||
portgroup.switchUuid = network_ref['dvsw']
|
||||
portgroup.portgroupKey = network_ref['dvpg']
|
||||
backing.port = portgroup
|
||||
else:
|
||||
backing = \
|
||||
client_factory.create('ns0:VirtualEthernetCardNetworkBackingInfo')
|
||||
backing.deviceName = network_name
|
||||
|
||||
connectable_spec = \
|
||||
client_factory.create('ns0:VirtualDeviceConnectInfo')
|
||||
connectable_spec.startConnected = True
|
||||
connectable_spec.allowGuestControl = True
|
||||
connectable_spec.connected = True
|
||||
|
||||
net_device.connectable = connectable_spec
|
||||
net_device.backing = backing
|
||||
|
||||
# The Server assigns a Key to the device. Here we pass a -ve temporary key.
|
||||
# -ve because actual keys are +ve numbers and we don't
|
||||
# want a clash with the key that server might associate with the device
|
||||
net_device.key = -47
|
||||
net_device.addressType = "manual"
|
||||
net_device.macAddress = mac_address
|
||||
net_device.wakeOnLanEnabled = True
|
||||
|
||||
network_spec.device = net_device
|
||||
return network_spec
|
||||
|
||||
|
||||
def get_vmdk_attach_config_spec(client_factory, disksize, file_path,
|
||||
adapter_type="lsiLogic"):
|
||||
"""Builds the vmdk attach config spec."""
|
||||
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
|
||||
# The controller Key pertains to the Key of the LSI Logic Controller, which
|
||||
# controls this Hard Disk
|
||||
device_config_spec = []
|
||||
# For IDE devices, there are these two default controllers created in the
|
||||
# VM having keys 200 and 201
|
||||
if adapter_type == "ide":
|
||||
controller_key = 200
|
||||
else:
|
||||
controller_key = -101
|
||||
controller_spec = create_controller_spec(client_factory,
|
||||
controller_key)
|
||||
device_config_spec.append(controller_spec)
|
||||
virtual_device_config_spec = create_virtual_disk_spec(client_factory,
|
||||
disksize, controller_key, file_path)
|
||||
|
||||
device_config_spec.append(virtual_device_config_spec)
|
||||
|
||||
config_spec.deviceChange = device_config_spec
|
||||
return config_spec
|
||||
|
||||
|
||||
def get_vmdk_file_path_and_adapter_type(client_factory, hardware_devices):
|
||||
"""Gets the vmdk file path and the storage adapter type."""
|
||||
if hardware_devices.__class__.__name__ == "ArrayOfVirtualDevice":
|
||||
hardware_devices = hardware_devices.VirtualDevice
|
||||
vmdk_file_path = None
|
||||
vmdk_controler_key = None
|
||||
|
||||
adapter_type_dict = {}
|
||||
for device in hardware_devices:
|
||||
if device.__class__.__name__ == "VirtualDisk" and \
|
||||
device.backing.__class__.__name__ \
|
||||
== "VirtualDiskFlatVer2BackingInfo":
|
||||
vmdk_file_path = device.backing.fileName
|
||||
vmdk_controler_key = device.controllerKey
|
||||
elif device.__class__.__name__ == "VirtualLsiLogicController":
|
||||
adapter_type_dict[device.key] = "lsiLogic"
|
||||
elif device.__class__.__name__ == "VirtualBusLogicController":
|
||||
adapter_type_dict[device.key] = "busLogic"
|
||||
elif device.__class__.__name__ == "VirtualIDEController":
|
||||
adapter_type_dict[device.key] = "ide"
|
||||
elif device.__class__.__name__ == "VirtualLsiLogicSASController":
|
||||
adapter_type_dict[device.key] = "lsiLogic"
|
||||
|
||||
adapter_type = adapter_type_dict.get(vmdk_controler_key, "")
|
||||
|
||||
return vmdk_file_path, adapter_type
|
||||
|
||||
|
||||
def get_copy_virtual_disk_spec(client_factory, adapter_type="lsilogic"):
|
||||
"""Builds the Virtual Disk copy spec."""
|
||||
dest_spec = client_factory.create('ns0:VirtualDiskSpec')
|
||||
dest_spec.adapterType = adapter_type
|
||||
dest_spec.diskType = "thick"
|
||||
return dest_spec
|
||||
|
||||
|
||||
def get_vmdk_create_spec(client_factory, size_in_kb, adapter_type="lsiLogic"):
|
||||
"""Builds the virtual disk create spec."""
|
||||
create_vmdk_spec = \
|
||||
client_factory.create('ns0:FileBackedVirtualDiskSpec')
|
||||
create_vmdk_spec.adapterType = adapter_type
|
||||
create_vmdk_spec.diskType = "thick"
|
||||
create_vmdk_spec.capacityKb = size_in_kb
|
||||
return create_vmdk_spec
|
||||
|
||||
|
||||
def create_virtual_disk_spec(client_factory, disksize, controller_key,
|
||||
file_path=None):
|
||||
"""
|
||||
Builds spec for the creation of a new/ attaching of an already existing
|
||||
Virtual Disk to the VM.
|
||||
"""
|
||||
virtual_device_config = \
|
||||
client_factory.create('ns0:VirtualDeviceConfigSpec')
|
||||
virtual_device_config.operation = "add"
|
||||
if file_path is None:
|
||||
virtual_device_config.fileOperation = "create"
|
||||
|
||||
virtual_disk = client_factory.create('ns0:VirtualDisk')
|
||||
|
||||
disk_file_backing = \
|
||||
client_factory.create('ns0:VirtualDiskFlatVer2BackingInfo')
|
||||
disk_file_backing.diskMode = "persistent"
|
||||
disk_file_backing.thinProvisioned = False
|
||||
if file_path is not None:
|
||||
disk_file_backing.fileName = file_path
|
||||
else:
|
||||
disk_file_backing.fileName = ""
|
||||
|
||||
connectable_spec = client_factory.create('ns0:VirtualDeviceConnectInfo')
|
||||
connectable_spec.startConnected = True
|
||||
connectable_spec.allowGuestControl = False
|
||||
connectable_spec.connected = True
|
||||
|
||||
virtual_disk.backing = disk_file_backing
|
||||
virtual_disk.connectable = connectable_spec
|
||||
|
||||
# The Server assigns a Key to the device. Here we pass a -ve random key.
|
||||
# -ve because actual keys are +ve numbers and we don't
|
||||
# want a clash with the key that server might associate with the device
|
||||
virtual_disk.key = -100
|
||||
virtual_disk.controllerKey = controller_key
|
||||
virtual_disk.unitNumber = 0
|
||||
virtual_disk.capacityInKB = disksize
|
||||
|
||||
virtual_device_config.device = virtual_disk
|
||||
|
||||
return virtual_device_config
|
||||
|
||||
|
||||
def get_dummy_vm_create_spec(client_factory, name, data_store_name):
|
||||
"""Builds the dummy VM create spec."""
|
||||
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
|
||||
config_spec.name = name
|
||||
config_spec.guestId = "otherGuest"
|
||||
|
||||
vm_file_info = client_factory.create('ns0:VirtualMachineFileInfo')
|
||||
vm_file_info.vmPathName = "[" + data_store_name + "]"
|
||||
config_spec.files = vm_file_info
|
||||
|
||||
tools_info = client_factory.create('ns0:ToolsConfigInfo')
|
||||
tools_info.afterPowerOn = True
|
||||
tools_info.afterResume = True
|
||||
tools_info.beforeGuestStandby = True
|
||||
tools_info.beforeGuestShutdown = True
|
||||
tools_info.beforeGuestReboot = True
|
||||
|
||||
config_spec.tools = tools_info
|
||||
config_spec.numCPUs = 1
|
||||
config_spec.memoryMB = 4
|
||||
|
||||
controller_key = -101
|
||||
controller_spec = create_controller_spec(client_factory, controller_key)
|
||||
disk_spec = create_virtual_disk_spec(client_factory, 1024, controller_key)
|
||||
|
||||
device_config_spec = [controller_spec, disk_spec]
|
||||
|
||||
config_spec.deviceChange = device_config_spec
|
||||
return config_spec
|
||||
|
||||
|
||||
def get_machine_id_change_spec(client_factory, machine_id_str):
|
||||
"""Builds the machine id change config spec."""
|
||||
virtual_machine_config_spec = \
|
||||
client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
|
||||
opt = client_factory.create('ns0:OptionValue')
|
||||
opt.key = "machine.id"
|
||||
opt.value = machine_id_str
|
||||
virtual_machine_config_spec.extraConfig = [opt]
|
||||
return virtual_machine_config_spec
|
||||
|
||||
|
||||
def get_add_vswitch_port_group_spec(client_factory, vswitch_name,
|
||||
port_group_name, vlan_id):
|
||||
"""Builds the virtual switch port group add spec."""
|
||||
vswitch_port_group_spec = client_factory.create('ns0:HostPortGroupSpec')
|
||||
vswitch_port_group_spec.name = port_group_name
|
||||
vswitch_port_group_spec.vswitchName = vswitch_name
|
||||
|
||||
# VLAN ID of 0 means that VLAN tagging is not to be done for the network.
|
||||
vswitch_port_group_spec.vlanId = int(vlan_id)
|
||||
|
||||
policy = client_factory.create('ns0:HostNetworkPolicy')
|
||||
nicteaming = client_factory.create('ns0:HostNicTeamingPolicy')
|
||||
nicteaming.notifySwitches = True
|
||||
policy.nicTeaming = nicteaming
|
||||
|
||||
vswitch_port_group_spec.policy = policy
|
||||
return vswitch_port_group_spec
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
The VMware API VM utility module to build SOAP object specs.
|
||||
"""
|
||||
|
||||
|
||||
def build_datastore_path(datastore_name, path):
|
||||
"""Build the datastore compliant path."""
|
||||
return "[%s] %s" % (datastore_name, path)
|
||||
|
||||
|
||||
def split_datastore_path(datastore_path):
|
||||
"""
|
||||
Split the VMWare style datastore path to get the Datastore
|
||||
name and the entity path.
|
||||
"""
|
||||
spl = datastore_path.split('[', 1)[1].split(']', 1)
|
||||
path = ""
|
||||
if len(spl) == 1:
|
||||
datastore_url = spl[0]
|
||||
else:
|
||||
datastore_url, path = spl
|
||||
return datastore_url, path.strip()
|
||||
|
||||
|
||||
def get_vm_create_spec(client_factory, instance, data_store_name,
|
||||
vif_infos, os_type="otherGuest"):
|
||||
"""Builds the VM Create spec."""
|
||||
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
config_spec.name = instance.name
|
||||
config_spec.guestId = os_type
|
||||
|
||||
vm_file_info = client_factory.create('ns0:VirtualMachineFileInfo')
|
||||
vm_file_info.vmPathName = "[" + data_store_name + "]"
|
||||
config_spec.files = vm_file_info
|
||||
|
||||
tools_info = client_factory.create('ns0:ToolsConfigInfo')
|
||||
tools_info.afterPowerOn = True
|
||||
tools_info.afterResume = True
|
||||
tools_info.beforeGuestStandby = True
|
||||
tools_info.beforeGuestShutdown = True
|
||||
tools_info.beforeGuestReboot = True
|
||||
|
||||
config_spec.tools = tools_info
|
||||
config_spec.numCPUs = int(instance.vcpus)
|
||||
config_spec.memoryMB = int(instance.memory_mb)
|
||||
|
||||
vif_spec_list = []
|
||||
for vif_info in vif_infos:
|
||||
vif_spec = create_network_spec(client_factory, vif_info)
|
||||
vif_spec_list.append(vif_spec)
|
||||
|
||||
device_config_spec = vif_spec_list
|
||||
|
||||
config_spec.deviceChange = device_config_spec
|
||||
return config_spec
|
||||
|
||||
|
||||
def create_controller_spec(client_factory, key):
|
||||
"""
|
||||
Builds a Config Spec for the LSI Logic Controller's addition
|
||||
which acts as the controller for the virtual hard disk to be attached
|
||||
to the VM.
|
||||
"""
|
||||
# Create a controller for the Virtual Hard Disk
|
||||
virtual_device_config = \
|
||||
client_factory.create('ns0:VirtualDeviceConfigSpec')
|
||||
virtual_device_config.operation = "add"
|
||||
virtual_lsi = \
|
||||
client_factory.create('ns0:VirtualLsiLogicController')
|
||||
virtual_lsi.key = key
|
||||
virtual_lsi.busNumber = 0
|
||||
virtual_lsi.sharedBus = "noSharing"
|
||||
virtual_device_config.device = virtual_lsi
|
||||
return virtual_device_config
|
||||
|
||||
|
||||
def create_network_spec(client_factory, vif_info):
|
||||
"""
|
||||
Builds a config spec for the addition of a new network
|
||||
adapter to the VM.
|
||||
"""
|
||||
network_spec = \
|
||||
client_factory.create('ns0:VirtualDeviceConfigSpec')
|
||||
network_spec.operation = "add"
|
||||
|
||||
# Get the recommended card type for the VM based on the guest OS of the VM
|
||||
net_device = client_factory.create('ns0:VirtualPCNet32')
|
||||
|
||||
# NOTE(asomya): Only works on ESXi if the portgroup binding is set to
|
||||
# ephemeral. Invalid configuration if set to static and the NIC does
|
||||
# not come up on boot if set to dynamic.
|
||||
network_ref = vif_info['network_ref']
|
||||
network_name = vif_info['network_name']
|
||||
mac_address = vif_info['mac_address']
|
||||
backing = None
|
||||
if (network_ref and
|
||||
network_ref['type'] == "DistributedVirtualPortgroup"):
|
||||
backing_name = \
|
||||
'ns0:VirtualEthernetCardDistributedVirtualPortBackingInfo'
|
||||
backing = \
|
||||
client_factory.create(backing_name)
|
||||
portgroup = \
|
||||
client_factory.create('ns0:DistributedVirtualSwitchPortConnection')
|
||||
portgroup.switchUuid = network_ref['dvsw']
|
||||
portgroup.portgroupKey = network_ref['dvpg']
|
||||
backing.port = portgroup
|
||||
else:
|
||||
backing = \
|
||||
client_factory.create('ns0:VirtualEthernetCardNetworkBackingInfo')
|
||||
backing.deviceName = network_name
|
||||
|
||||
connectable_spec = \
|
||||
client_factory.create('ns0:VirtualDeviceConnectInfo')
|
||||
connectable_spec.startConnected = True
|
||||
connectable_spec.allowGuestControl = True
|
||||
connectable_spec.connected = True
|
||||
|
||||
net_device.connectable = connectable_spec
|
||||
net_device.backing = backing
|
||||
|
||||
# The Server assigns a Key to the device. Here we pass a -ve temporary key.
|
||||
# -ve because actual keys are +ve numbers and we don't
|
||||
# want a clash with the key that server might associate with the device
|
||||
net_device.key = -47
|
||||
net_device.addressType = "manual"
|
||||
net_device.macAddress = mac_address
|
||||
net_device.wakeOnLanEnabled = True
|
||||
|
||||
network_spec.device = net_device
|
||||
return network_spec
|
||||
|
||||
|
||||
def get_vmdk_attach_config_spec(client_factory, disksize, file_path,
|
||||
adapter_type="lsiLogic"):
|
||||
"""Builds the vmdk attach config spec."""
|
||||
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
|
||||
# The controller Key pertains to the Key of the LSI Logic Controller, which
|
||||
# controls this Hard Disk
|
||||
device_config_spec = []
|
||||
# For IDE devices, there are these two default controllers created in the
|
||||
# VM having keys 200 and 201
|
||||
if adapter_type == "ide":
|
||||
controller_key = 200
|
||||
else:
|
||||
controller_key = -101
|
||||
controller_spec = create_controller_spec(client_factory,
|
||||
controller_key)
|
||||
device_config_spec.append(controller_spec)
|
||||
virtual_device_config_spec = create_virtual_disk_spec(client_factory,
|
||||
disksize, controller_key, file_path)
|
||||
|
||||
device_config_spec.append(virtual_device_config_spec)
|
||||
|
||||
config_spec.deviceChange = device_config_spec
|
||||
return config_spec
|
||||
|
||||
|
||||
def get_vmdk_file_path_and_adapter_type(client_factory, hardware_devices):
|
||||
"""Gets the vmdk file path and the storage adapter type."""
|
||||
if hardware_devices.__class__.__name__ == "ArrayOfVirtualDevice":
|
||||
hardware_devices = hardware_devices.VirtualDevice
|
||||
vmdk_file_path = None
|
||||
vmdk_controler_key = None
|
||||
|
||||
adapter_type_dict = {}
|
||||
for device in hardware_devices:
|
||||
if device.__class__.__name__ == "VirtualDisk" and \
|
||||
device.backing.__class__.__name__ \
|
||||
== "VirtualDiskFlatVer2BackingInfo":
|
||||
vmdk_file_path = device.backing.fileName
|
||||
vmdk_controler_key = device.controllerKey
|
||||
elif device.__class__.__name__ == "VirtualLsiLogicController":
|
||||
adapter_type_dict[device.key] = "lsiLogic"
|
||||
elif device.__class__.__name__ == "VirtualBusLogicController":
|
||||
adapter_type_dict[device.key] = "busLogic"
|
||||
elif device.__class__.__name__ == "VirtualIDEController":
|
||||
adapter_type_dict[device.key] = "ide"
|
||||
elif device.__class__.__name__ == "VirtualLsiLogicSASController":
|
||||
adapter_type_dict[device.key] = "lsiLogic"
|
||||
|
||||
adapter_type = adapter_type_dict.get(vmdk_controler_key, "")
|
||||
|
||||
return vmdk_file_path, adapter_type
|
||||
|
||||
|
||||
def get_copy_virtual_disk_spec(client_factory, adapter_type="lsilogic"):
|
||||
"""Builds the Virtual Disk copy spec."""
|
||||
dest_spec = client_factory.create('ns0:VirtualDiskSpec')
|
||||
dest_spec.adapterType = adapter_type
|
||||
dest_spec.diskType = "thick"
|
||||
return dest_spec
|
||||
|
||||
|
||||
def get_vmdk_create_spec(client_factory, size_in_kb, adapter_type="lsiLogic"):
|
||||
"""Builds the virtual disk create spec."""
|
||||
create_vmdk_spec = \
|
||||
client_factory.create('ns0:FileBackedVirtualDiskSpec')
|
||||
create_vmdk_spec.adapterType = adapter_type
|
||||
create_vmdk_spec.diskType = "thick"
|
||||
create_vmdk_spec.capacityKb = size_in_kb
|
||||
return create_vmdk_spec
|
||||
|
||||
|
||||
def create_virtual_disk_spec(client_factory, disksize, controller_key,
|
||||
file_path=None):
|
||||
"""
|
||||
Builds spec for the creation of a new/ attaching of an already existing
|
||||
Virtual Disk to the VM.
|
||||
"""
|
||||
virtual_device_config = \
|
||||
client_factory.create('ns0:VirtualDeviceConfigSpec')
|
||||
virtual_device_config.operation = "add"
|
||||
if file_path is None:
|
||||
virtual_device_config.fileOperation = "create"
|
||||
|
||||
virtual_disk = client_factory.create('ns0:VirtualDisk')
|
||||
|
||||
disk_file_backing = \
|
||||
client_factory.create('ns0:VirtualDiskFlatVer2BackingInfo')
|
||||
disk_file_backing.diskMode = "persistent"
|
||||
disk_file_backing.thinProvisioned = False
|
||||
if file_path is not None:
|
||||
disk_file_backing.fileName = file_path
|
||||
else:
|
||||
disk_file_backing.fileName = ""
|
||||
|
||||
connectable_spec = client_factory.create('ns0:VirtualDeviceConnectInfo')
|
||||
connectable_spec.startConnected = True
|
||||
connectable_spec.allowGuestControl = False
|
||||
connectable_spec.connected = True
|
||||
|
||||
virtual_disk.backing = disk_file_backing
|
||||
virtual_disk.connectable = connectable_spec
|
||||
|
||||
# The Server assigns a Key to the device. Here we pass a -ve random key.
|
||||
# -ve because actual keys are +ve numbers and we don't
|
||||
# want a clash with the key that server might associate with the device
|
||||
virtual_disk.key = -100
|
||||
virtual_disk.controllerKey = controller_key
|
||||
virtual_disk.unitNumber = 0
|
||||
virtual_disk.capacityInKB = disksize
|
||||
|
||||
virtual_device_config.device = virtual_disk
|
||||
|
||||
return virtual_device_config
|
||||
|
||||
|
||||
def get_dummy_vm_create_spec(client_factory, name, data_store_name):
|
||||
"""Builds the dummy VM create spec."""
|
||||
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
|
||||
config_spec.name = name
|
||||
config_spec.guestId = "otherGuest"
|
||||
|
||||
vm_file_info = client_factory.create('ns0:VirtualMachineFileInfo')
|
||||
vm_file_info.vmPathName = "[" + data_store_name + "]"
|
||||
config_spec.files = vm_file_info
|
||||
|
||||
tools_info = client_factory.create('ns0:ToolsConfigInfo')
|
||||
tools_info.afterPowerOn = True
|
||||
tools_info.afterResume = True
|
||||
tools_info.beforeGuestStandby = True
|
||||
tools_info.beforeGuestShutdown = True
|
||||
tools_info.beforeGuestReboot = True
|
||||
|
||||
config_spec.tools = tools_info
|
||||
config_spec.numCPUs = 1
|
||||
config_spec.memoryMB = 4
|
||||
|
||||
controller_key = -101
|
||||
controller_spec = create_controller_spec(client_factory, controller_key)
|
||||
disk_spec = create_virtual_disk_spec(client_factory, 1024, controller_key)
|
||||
|
||||
device_config_spec = [controller_spec, disk_spec]
|
||||
|
||||
config_spec.deviceChange = device_config_spec
|
||||
return config_spec
|
||||
|
||||
|
||||
def get_machine_id_change_spec(client_factory, machine_id_str):
|
||||
"""Builds the machine id change config spec."""
|
||||
virtual_machine_config_spec = \
|
||||
client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
|
||||
opt = client_factory.create('ns0:OptionValue')
|
||||
opt.key = "machine.id"
|
||||
opt.value = machine_id_str
|
||||
virtual_machine_config_spec.extraConfig = [opt]
|
||||
return virtual_machine_config_spec
|
||||
|
||||
|
||||
def get_add_vswitch_port_group_spec(client_factory, vswitch_name,
|
||||
port_group_name, vlan_id):
|
||||
"""Builds the virtual switch port group add spec."""
|
||||
vswitch_port_group_spec = client_factory.create('ns0:HostPortGroupSpec')
|
||||
vswitch_port_group_spec.name = port_group_name
|
||||
vswitch_port_group_spec.vswitchName = vswitch_name
|
||||
|
||||
# VLAN ID of 0 means that VLAN tagging is not to be done for the network.
|
||||
vswitch_port_group_spec.vlanId = int(vlan_id)
|
||||
|
||||
policy = client_factory.create('ns0:HostNetworkPolicy')
|
||||
nicteaming = client_factory.create('ns0:HostNicTeamingPolicy')
|
||||
nicteaming.notifySwitches = True
|
||||
policy.nicTeaming = nicteaming
|
||||
|
||||
vswitch_port_group_spec.policy = policy
|
||||
return vswitch_port_group_spec
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,145 +1,145 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Utility functions for Image transfer.
|
||||
"""
|
||||
|
||||
from nova import exception
|
||||
from nova.image import glance
|
||||
from nova import log as logging
|
||||
from nova.virt.vmwareapi import io_util
|
||||
from nova.virt.vmwareapi import read_write_util
|
||||
|
||||
LOG = logging.getLogger("nova.virt.vmwareapi.vmware_images")
|
||||
|
||||
QUEUE_BUFFER_SIZE = 10
|
||||
|
||||
|
||||
def start_transfer(read_file_handle, data_size, write_file_handle=None,
|
||||
glance_client=None, image_id=None, image_meta=None):
|
||||
"""Start the data transfer from the reader to the writer.
|
||||
Reader writes to the pipe and the writer reads from the pipe. This means
|
||||
that the total transfer time boils down to the slower of the read/write
|
||||
and not the addition of the two times."""
|
||||
|
||||
if not image_meta:
|
||||
image_meta = {}
|
||||
|
||||
# The pipe that acts as an intermediate store of data for reader to write
|
||||
# to and writer to grab from.
|
||||
thread_safe_pipe = io_util.ThreadSafePipe(QUEUE_BUFFER_SIZE, data_size)
|
||||
# The read thread. In case of glance it is the instance of the
|
||||
# GlanceFileRead class. The glance client read returns an iterator
|
||||
# and this class wraps that iterator to provide datachunks in calls
|
||||
# to read.
|
||||
read_thread = io_util.IOThread(read_file_handle, thread_safe_pipe)
|
||||
|
||||
# In case of Glance - VMWare transfer, we just need a handle to the
|
||||
# HTTP Connection that is to send transfer data to the VMWare datastore.
|
||||
if write_file_handle:
|
||||
write_thread = io_util.IOThread(thread_safe_pipe, write_file_handle)
|
||||
# In case of VMWare - Glance transfer, we relinquish VMWare HTTP file read
|
||||
# handle to Glance Client instance, but to be sure of the transfer we need
|
||||
# to be sure of the status of the image on glnace changing to active.
|
||||
# The GlanceWriteThread handles the same for us.
|
||||
elif glance_client and image_id:
|
||||
write_thread = io_util.GlanceWriteThread(thread_safe_pipe,
|
||||
glance_client, image_id, image_meta)
|
||||
# Start the read and write threads.
|
||||
read_event = read_thread.start()
|
||||
write_event = write_thread.start()
|
||||
try:
|
||||
# Wait on the read and write events to signal their end
|
||||
read_event.wait()
|
||||
write_event.wait()
|
||||
except Exception, exc:
|
||||
# In case of any of the reads or writes raising an exception,
|
||||
# stop the threads so that we un-necessarily don't keep the other one
|
||||
# waiting.
|
||||
read_thread.stop()
|
||||
write_thread.stop()
|
||||
|
||||
# Log and raise the exception.
|
||||
LOG.exception(exc)
|
||||
raise exception.Error(exc)
|
||||
finally:
|
||||
# No matter what, try closing the read and write handles, if it so
|
||||
# applies.
|
||||
read_file_handle.close()
|
||||
if write_file_handle:
|
||||
write_file_handle.close()
|
||||
|
||||
|
||||
def fetch_image(context, image, instance, **kwargs):
|
||||
"""Download image from the glance image server."""
|
||||
LOG.debug(_("Downloading image %s from glance image server") % image)
|
||||
(glance_client, image_id) = glance.get_glance_client(context, image)
|
||||
metadata, read_iter = glance_client.get_image(image_id)
|
||||
read_file_handle = read_write_util.GlanceFileRead(read_iter)
|
||||
file_size = int(metadata['size'])
|
||||
write_file_handle = read_write_util.VMWareHTTPWriteFile(
|
||||
kwargs.get("host"),
|
||||
kwargs.get("data_center_name"),
|
||||
kwargs.get("datastore_name"),
|
||||
kwargs.get("cookies"),
|
||||
kwargs.get("file_path"),
|
||||
file_size)
|
||||
start_transfer(read_file_handle, file_size,
|
||||
write_file_handle=write_file_handle)
|
||||
LOG.debug(_("Downloaded image %s from glance image server") % image)
|
||||
|
||||
|
||||
def upload_image(context, image, instance, **kwargs):
|
||||
"""Upload the snapshotted vm disk file to Glance image server."""
|
||||
LOG.debug(_("Uploading image %s to the Glance image server") % image)
|
||||
read_file_handle = read_write_util.VmWareHTTPReadFile(
|
||||
kwargs.get("host"),
|
||||
kwargs.get("data_center_name"),
|
||||
kwargs.get("datastore_name"),
|
||||
kwargs.get("cookies"),
|
||||
kwargs.get("file_path"))
|
||||
file_size = read_file_handle.get_size()
|
||||
(glance_client, image_id) = glance.get_glance_client(context, image)
|
||||
# The properties and other fields that we need to set for the image.
|
||||
image_metadata = {"is_public": True,
|
||||
"disk_format": "vmdk",
|
||||
"container_format": "bare",
|
||||
"type": "vmdk",
|
||||
"properties": {"vmware_adaptertype":
|
||||
kwargs.get("adapter_type"),
|
||||
"vmware_ostype": kwargs.get("os_type"),
|
||||
"vmware_image_version":
|
||||
kwargs.get("image_version")}}
|
||||
start_transfer(read_file_handle, file_size, glance_client=glance_client,
|
||||
image_id=image_id, image_meta=image_metadata)
|
||||
LOG.debug(_("Uploaded image %s to the Glance image server") % image)
|
||||
|
||||
|
||||
def get_vmdk_size_and_properties(context, image, instance):
|
||||
"""
|
||||
Get size of the vmdk file that is to be downloaded for attach in spawn.
|
||||
Need this to create the dummy virtual disk for the meta-data file. The
|
||||
geometry of the disk created depends on the size.
|
||||
"""
|
||||
|
||||
LOG.debug(_("Getting image size for the image %s") % image)
|
||||
(glance_client, image_id) = glance.get_glance_client(context, image)
|
||||
meta_data = glance_client.get_image_meta(image_id)
|
||||
size, properties = meta_data["size"], meta_data["properties"]
|
||||
LOG.debug(_("Got image size of %(size)s for the image %(image)s") %
|
||||
locals())
|
||||
return size, properties
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Utility functions for Image transfer.
|
||||
"""
|
||||
|
||||
from nova import exception
|
||||
from nova.image import glance
|
||||
from nova import log as logging
|
||||
from nova.virt.vmwareapi import io_util
|
||||
from nova.virt.vmwareapi import read_write_util
|
||||
|
||||
LOG = logging.getLogger("nova.virt.vmwareapi.vmware_images")
|
||||
|
||||
QUEUE_BUFFER_SIZE = 10
|
||||
|
||||
|
||||
def start_transfer(read_file_handle, data_size, write_file_handle=None,
|
||||
glance_client=None, image_id=None, image_meta=None):
|
||||
"""Start the data transfer from the reader to the writer.
|
||||
Reader writes to the pipe and the writer reads from the pipe. This means
|
||||
that the total transfer time boils down to the slower of the read/write
|
||||
and not the addition of the two times."""
|
||||
|
||||
if not image_meta:
|
||||
image_meta = {}
|
||||
|
||||
# The pipe that acts as an intermediate store of data for reader to write
|
||||
# to and writer to grab from.
|
||||
thread_safe_pipe = io_util.ThreadSafePipe(QUEUE_BUFFER_SIZE, data_size)
|
||||
# The read thread. In case of glance it is the instance of the
|
||||
# GlanceFileRead class. The glance client read returns an iterator
|
||||
# and this class wraps that iterator to provide datachunks in calls
|
||||
# to read.
|
||||
read_thread = io_util.IOThread(read_file_handle, thread_safe_pipe)
|
||||
|
||||
# In case of Glance - VMWare transfer, we just need a handle to the
|
||||
# HTTP Connection that is to send transfer data to the VMWare datastore.
|
||||
if write_file_handle:
|
||||
write_thread = io_util.IOThread(thread_safe_pipe, write_file_handle)
|
||||
# In case of VMWare - Glance transfer, we relinquish VMWare HTTP file read
|
||||
# handle to Glance Client instance, but to be sure of the transfer we need
|
||||
# to be sure of the status of the image on glnace changing to active.
|
||||
# The GlanceWriteThread handles the same for us.
|
||||
elif glance_client and image_id:
|
||||
write_thread = io_util.GlanceWriteThread(thread_safe_pipe,
|
||||
glance_client, image_id, image_meta)
|
||||
# Start the read and write threads.
|
||||
read_event = read_thread.start()
|
||||
write_event = write_thread.start()
|
||||
try:
|
||||
# Wait on the read and write events to signal their end
|
||||
read_event.wait()
|
||||
write_event.wait()
|
||||
except Exception, exc:
|
||||
# In case of any of the reads or writes raising an exception,
|
||||
# stop the threads so that we un-necessarily don't keep the other one
|
||||
# waiting.
|
||||
read_thread.stop()
|
||||
write_thread.stop()
|
||||
|
||||
# Log and raise the exception.
|
||||
LOG.exception(exc)
|
||||
raise exception.Error(exc)
|
||||
finally:
|
||||
# No matter what, try closing the read and write handles, if it so
|
||||
# applies.
|
||||
read_file_handle.close()
|
||||
if write_file_handle:
|
||||
write_file_handle.close()
|
||||
|
||||
|
||||
def fetch_image(context, image, instance, **kwargs):
|
||||
"""Download image from the glance image server."""
|
||||
LOG.debug(_("Downloading image %s from glance image server") % image)
|
||||
(glance_client, image_id) = glance.get_glance_client(context, image)
|
||||
metadata, read_iter = glance_client.get_image(image_id)
|
||||
read_file_handle = read_write_util.GlanceFileRead(read_iter)
|
||||
file_size = int(metadata['size'])
|
||||
write_file_handle = read_write_util.VMWareHTTPWriteFile(
|
||||
kwargs.get("host"),
|
||||
kwargs.get("data_center_name"),
|
||||
kwargs.get("datastore_name"),
|
||||
kwargs.get("cookies"),
|
||||
kwargs.get("file_path"),
|
||||
file_size)
|
||||
start_transfer(read_file_handle, file_size,
|
||||
write_file_handle=write_file_handle)
|
||||
LOG.debug(_("Downloaded image %s from glance image server") % image)
|
||||
|
||||
|
||||
def upload_image(context, image, instance, **kwargs):
|
||||
"""Upload the snapshotted vm disk file to Glance image server."""
|
||||
LOG.debug(_("Uploading image %s to the Glance image server") % image)
|
||||
read_file_handle = read_write_util.VmWareHTTPReadFile(
|
||||
kwargs.get("host"),
|
||||
kwargs.get("data_center_name"),
|
||||
kwargs.get("datastore_name"),
|
||||
kwargs.get("cookies"),
|
||||
kwargs.get("file_path"))
|
||||
file_size = read_file_handle.get_size()
|
||||
(glance_client, image_id) = glance.get_glance_client(context, image)
|
||||
# The properties and other fields that we need to set for the image.
|
||||
image_metadata = {"is_public": True,
|
||||
"disk_format": "vmdk",
|
||||
"container_format": "bare",
|
||||
"type": "vmdk",
|
||||
"properties": {"vmware_adaptertype":
|
||||
kwargs.get("adapter_type"),
|
||||
"vmware_ostype": kwargs.get("os_type"),
|
||||
"vmware_image_version":
|
||||
kwargs.get("image_version")}}
|
||||
start_transfer(read_file_handle, file_size, glance_client=glance_client,
|
||||
image_id=image_id, image_meta=image_metadata)
|
||||
LOG.debug(_("Uploaded image %s to the Glance image server") % image)
|
||||
|
||||
|
||||
def get_vmdk_size_and_properties(context, image, instance):
|
||||
"""
|
||||
Get size of the vmdk file that is to be downloaded for attach in spawn.
|
||||
Need this to create the dummy virtual disk for the meta-data file. The
|
||||
geometry of the disk created depends on the size.
|
||||
"""
|
||||
|
||||
LOG.debug(_("Getting image size for the image %s") % image)
|
||||
(glance_client, image_id) = glance.get_glance_client(context, image)
|
||||
meta_data = glance_client.get_image_meta(image_id)
|
||||
size, properties = meta_data["size"], meta_data["properties"]
|
||||
LOG.debug(_("Got image size of %(size)s for the image %(image)s") %
|
||||
locals())
|
||||
return size, properties
|
||||
|
Loading…
x
Reference in New Issue
Block a user