Adds extend volume plugin files

This patch includes the VDS Python wrapper.
This commit is contained in:
Alessandro Pilotti 2013-04-03 15:31:52 +03:00
parent 100e5b6650
commit 083cae60da
2 changed files with 493 additions and 0 deletions

View File

@ -0,0 +1,168 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2013 Cloudbase Solutions Srl
#
# 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.
import ctypes
import re
from cloudbaseinit.openstack.common import cfg
from cloudbaseinit.openstack.common import log as logging
from cloudbaseinit.plugins import base
from cloudbaseinit.plugins.windows import vds
ole32 = ctypes.windll.ole32
ole32.CoTaskMemFree.restype = None
ole32.CoTaskMemFree.argtypes = [ctypes.c_void_p]
opts = [
cfg.ListOpt('volumes_to_extend',
default=None,
help='List of volumes that need to be extended '
'if contiguous space is available on the disk. By default '
'all the available volumes can be extended. Volumes must '
'be specified using a comma separated list of volume indexes, '
'e.g.: "1,2"'),
]
CONF = cfg.CONF
CONF.register_opts(opts)
LOG = logging.getLogger(__name__)
class ExtendVolumesPlugin(base.BasePlugin):
def _extend_volumes(self, pack, volume_idxs=None):
enum = pack.QueryVolumes()
while True:
(unk, c) = enum.Next(1)
if not c:
break
volume = unk.QueryInterface(vds.IVdsVolume)
volume_prop = volume.GetProperties()
try:
extend_volume = True
if volume_idxs is not None:
volume_name = ctypes.wstring_at(volume_prop.pwszName)
volume_idx = self._get_volume_index(volume_name)
if not volume_idx in volume_idxs:
extend_volume = False
if extend_volume:
self._extend_volume(pack, volume, volume_prop)
finally:
ole32.CoTaskMemFree(volume_prop.pwszName)
def _get_volume_index(self, volume_name):
m = re.match(r"[^0-9]+([0-9]+)$", volume_name)
if m:
return int(m.group(1))
def _extend_volume(self, pack, volume, volume_prop):
volume_extents = self._get_volume_extents_to_resize(pack,
volume_prop.id)
input_disks = []
for (volume_extent, volume_extend_size) in volume_extents:
input_disk = vds.VDS_INPUT_DISK()
input_disks.append(input_disk)
input_disk.diskId = volume_extent.diskId
input_disk.memberIdx = volume_extent.memberIdx
input_disk.plexId = volume_extent.plexId
input_disk.ullSize = volume_extend_size
if input_disks:
extend_size = sum([i.ullSize for i in input_disks])
volume_name = ctypes.wstring_at(volume_prop.pwszName)
LOG.info('Extending volume "%s" with %s bytes' %
(volume_name, extend_size))
input_disks_ar = (vds.VDS_INPUT_DISK *
len(input_disks))(*input_disks)
async = volume.Extend(input_disks_ar, len(input_disks))
async.Wait()
def _get_volume_extents_to_resize(self, pack, volume_id):
volume_extents = []
enum = pack.QueryDisks()
while True:
(unk, c) = enum.Next(1)
if not c:
break
disk = unk.QueryInterface(vds.IVdsDisk)
(extents_p, num_extents) = disk.QueryExtents()
try:
extents_array_type = vds.VDS_DISK_EXTENT * num_extents
extents_array = extents_array_type.from_address(
ctypes.addressof(extents_p.contents))
volume_extent_extend_size = None
for extent in extents_array:
if extent.volumeId == volume_id:
# Copy the extent in order to return it safely
# after the source is deallocated
extent_copy = vds.VDS_DISK_EXTENT()
ctypes.pointer(extent_copy)[0] = extent
volume_extent_extend_size = [extent_copy, 0]
volume_extents.append(volume_extent_extend_size)
elif (volume_extent_extend_size and
extent.type == vds.VDS_DET_FREE):
volume_extent_extend_size[1] += extent.ullSize
else:
volume_extent_extend_size = None
finally:
ole32.CoTaskMemFree(extents_p)
# Return only the extents that need to be resized
return [ve for ve in volume_extents if ve[1] > 0]
def _query_providers(self, svc):
providers = []
enum = svc.QueryProviders(vds.VDS_QUERY_SOFTWARE_PROVIDERS)
while True:
(unk, c) = enum.Next(1)
if not c:
break
providers.append(unk.QueryInterface(vds.IVdsSwProvider))
return providers
def _query_packs(self, provider):
packs = []
enum = provider.QueryPacks()
while True:
(unk, c) = enum.Next(1)
if not c:
break
packs.append(unk.QueryInterface(vds.IVdsPack))
return packs
def _get_volumes_to_extend(self):
if CONF.volumes_to_extend is not None:
return map(int, CONF.volumes_to_extend)
def execute(self, service):
svc = vds.load_vds_service()
providers = self._query_providers(svc)
volumes_to_extend = self._get_volumes_to_extend()
for provider in providers:
packs = self._query_packs(provider)
for pack in packs:
self._extend_volumes(pack, volumes_to_extend)

View File

@ -0,0 +1,325 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2013 Cloudbase Solutions Srl
#
# 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.
import comtypes
import ctypes
from comtypes import client
from ctypes import wintypes
VDS_QUERY_SOFTWARE_PROVIDERS = 1
VDS_DET_FREE = 1
CLSID_VdsLoader = '{9C38ED61-D565-4728-AEEE-C80952F0ECDE}'
msvcrt = ctypes.cdll.msvcrt
msvcrt.memcmp.restype = ctypes.c_int
msvcrt.memcmp.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint]
class GUID(ctypes.Structure):
_fields_ = [
("data1", ctypes.wintypes.DWORD),
("data2", ctypes.wintypes.WORD),
("data3", ctypes.wintypes.WORD),
("data4", ctypes.c_byte * 8)]
def __eq__(self, other):
if type(other) != GUID:
return False
return not msvcrt.memcmp(ctypes.addressof(self),
ctypes.addressof(other),
ctypes.sizeof(GUID))
def __ne__(self, other):
return not self.__eq__(other)
class VDS_DISK_PROP_SWITCH_TYPE(ctypes.Union):
_fields_ = [
("dwSignature", wintypes.DWORD),
("DiskGuid", GUID),
]
class VDS_DISK_PROP(ctypes.Structure):
_fields_ = [
("id", GUID),
("status", ctypes.c_int),
("ReserveMode", ctypes.c_int),
("health", ctypes.c_int),
("dwDeviceType", wintypes.DWORD),
("dwMediaType", wintypes.DWORD),
("ullSize", wintypes.ULARGE_INTEGER),
("ulBytesPerSector", wintypes.ULONG),
("ulSectorsPerTrack", wintypes.ULONG),
("ulTracksPerCylinder", wintypes.ULONG),
("ulFlags", wintypes.ULONG),
("BusType", ctypes.c_int),
("PartitionStyle", ctypes.c_int),
("switch_type", VDS_DISK_PROP_SWITCH_TYPE),
("pwszDiskAddress", wintypes.c_void_p),
("pwszName", wintypes.c_void_p),
("pwszFriendlyName", wintypes.c_void_p),
("pwszAdaptorName", wintypes.c_void_p),
("pwszDevicePath", wintypes.c_void_p),
]
class VDS_DISK_EXTENT(ctypes.Structure):
_fields_ = [
("diskId", GUID),
("type", ctypes.c_int),
("ullOffset", wintypes.ULARGE_INTEGER),
("ullSize", wintypes.ULARGE_INTEGER),
("volumeId", GUID),
("plexId", GUID),
("memberIdx", wintypes.ULONG),
]
class VDS_VOLUME_PROP(ctypes.Structure):
_fields_ = [
("id", GUID),
("type", ctypes.c_int),
("status", ctypes.c_int),
("health", ctypes.c_int),
("TransitionState", ctypes.c_int),
("ullSize", wintypes.ULARGE_INTEGER),
("ulFlags", wintypes.ULONG),
("RecommendedFileSystemType", ctypes.c_int),
("pwszName", wintypes.c_void_p),
]
class VDS_INPUT_DISK(ctypes.Structure):
_fields_ = [
("diskId", GUID),
("ullSize", wintypes.ULARGE_INTEGER),
("plexId", GUID),
("memberIdx", wintypes.ULONG),
]
class VDS_ASYNC_OUTPUT_cp(ctypes.Structure):
_fields_ = [
("ullOffset", wintypes.ULARGE_INTEGER),
("volumeId", GUID),
]
class VDS_ASYNC_OUTPUT_cv(ctypes.Structure):
_fields_ = [
("pVolumeUnk", wintypes.ULARGE_INTEGER),
]
class VDS_ASYNC_OUTPUT_bvp(ctypes.Structure):
_fields_ = [
("pVolumeUnk", ctypes.POINTER(comtypes.IUnknown)),
]
class VDS_ASYNC_OUTPUT_sv(ctypes.Structure):
_fields_ = [
("ullReclaimedBytes", wintypes.ULARGE_INTEGER),
]
class VDS_ASYNC_OUTPUT_cl(ctypes.Structure):
_fields_ = [
("pLunUnk", ctypes.POINTER(comtypes.IUnknown)),
]
class VDS_ASYNC_OUTPUT_ct(ctypes.Structure):
_fields_ = [
("pTargetUnk", ctypes.POINTER(comtypes.IUnknown)),
]
class VDS_ASYNC_OUTPUT_cpg(ctypes.Structure):
_fields_ = [
("pPortalGroupUnk", ctypes.POINTER(comtypes.IUnknown)),
]
class VDS_ASYNC_OUTPUT_SWITCH_TYPE(ctypes.Union):
_fields_ = [
("cp", VDS_ASYNC_OUTPUT_cp),
("cv", VDS_ASYNC_OUTPUT_cv),
("bvp", VDS_ASYNC_OUTPUT_bvp),
("sv", VDS_ASYNC_OUTPUT_sv),
("cl", VDS_ASYNC_OUTPUT_cl),
("ct", VDS_ASYNC_OUTPUT_ct),
("cpg", VDS_ASYNC_OUTPUT_cpg),
]
class VDS_ASYNC_OUTPUT(ctypes.Structure):
_fields_ = [
("type", ctypes.c_int),
("switch_type", VDS_ASYNC_OUTPUT_SWITCH_TYPE),
]
class IEnumVdsObject(comtypes.IUnknown):
_iid_ = comtypes.GUID("{118610b7-8d94-4030-b5b8-500889788e4e}")
_methods_ = [
comtypes.COMMETHOD([], comtypes.HRESULT, 'Next',
(['in'], wintypes.ULONG, 'celt'),
(['out'], ctypes.POINTER(ctypes.POINTER(
comtypes.IUnknown)),
'ppObjectArray'),
(['out'], ctypes.POINTER(wintypes.ULONG),
'pcFetched')),
]
class IVdsService(comtypes.IUnknown):
_iid_ = comtypes.GUID("{0818a8ef-9ba9-40d8-a6f9-e22833cc771e}")
_methods_ = [
comtypes.COMMETHOD([], comtypes.HRESULT, 'IsServiceReady'),
comtypes.COMMETHOD([], comtypes.HRESULT, 'WaitForServiceReady'),
comtypes.COMMETHOD([], comtypes.HRESULT, 'GetProperties',
(['out'], ctypes.c_void_p, 'pServiceProp')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'QueryProviders',
(['in'], wintypes.DWORD, 'masks'),
(['out'],
ctypes.POINTER(ctypes.POINTER(IEnumVdsObject)),
'ppEnum'))
]
class IVdsServiceLoader(comtypes.IUnknown):
_iid_ = comtypes.GUID("{e0393303-90d4-4a97-ab71-e9b671ee2729}")
_methods_ = [
comtypes.COMMETHOD([], comtypes.HRESULT, 'LoadService',
(['in'], wintypes.LPCWSTR, 'pwszMachineName'),
(['out'],
ctypes.POINTER(ctypes.POINTER(IVdsService)),
'ppService'))
]
class IVdsSwProvider(comtypes.IUnknown):
_iid_ = comtypes.GUID("{9aa58360-ce33-4f92-b658-ed24b14425b8}")
_methods_ = [
comtypes.COMMETHOD([], comtypes.HRESULT, 'QueryPacks',
(['out'],
ctypes.POINTER(ctypes.POINTER(IEnumVdsObject)),
'ppEnum'))
]
class IVdsPack(comtypes.IUnknown):
_iid_ = comtypes.GUID("{3b69d7f5-9d94-4648-91ca-79939ba263bf}")
_methods_ = [
comtypes.COMMETHOD([], comtypes.HRESULT, 'GetProperties',
(['out'], ctypes.c_void_p, 'pPackProp')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'GetProvider',
(['out'],
ctypes.POINTER(ctypes.POINTER(comtypes.IUnknown)),
'ppProvider')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'QueryVolumes',
(['out'],
ctypes.POINTER(ctypes.POINTER(IEnumVdsObject)),
'ppEnum')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'QueryDisks',
(['out'],
ctypes.POINTER(ctypes.POINTER(IEnumVdsObject)),
'ppEnum'))
]
class IVdsDisk(comtypes.IUnknown):
_iid_ = comtypes.GUID("{07e5c822-f00c-47a1-8fce-b244da56fd06}")
_methods_ = [
comtypes.COMMETHOD([], comtypes.HRESULT, 'GetProperties',
(['out'], ctypes.POINTER(VDS_DISK_PROP),
'pDiskProperties')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'GetPack',
(['out'], ctypes.POINTER(ctypes.POINTER(IVdsPack)),
'ppPack')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'GetIdentificationData',
(['out'], ctypes.c_void_p, 'pLunInfo')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'QueryExtents',
(['out'], ctypes.POINTER(ctypes.POINTER(
VDS_DISK_EXTENT)),
'ppExtentArray'),
(['out'], ctypes.POINTER(wintypes.LONG),
'plNumberOfExtents')),
]
class IVdsAsync(comtypes.IUnknown):
_iid_ = comtypes.GUID("{d5d23b6d-5a55-4492-9889-397a3c2d2dbc}")
_methods_ = [
comtypes.COMMETHOD([], comtypes.HRESULT, 'Cancel'),
comtypes.COMMETHOD([], comtypes.HRESULT, 'Wait',
(['out'], ctypes.POINTER(
wintypes.HRESULT), 'pHrResult'),
(['out'], ctypes.POINTER(VDS_ASYNC_OUTPUT),
'pAsyncOut')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'QueryStatus',
(['out'], ctypes.POINTER(
wintypes.HRESULT), 'pHrResult'),
(['out'], ctypes.POINTER(wintypes.ULONG),
'pulPercentCompleted')),
]
class IVdsVolume(comtypes.IUnknown):
_iid_ = comtypes.GUID("{88306bb2-e71f-478c-86a2-79da200a0f11}")
_methods_ = [
comtypes.COMMETHOD([], comtypes.HRESULT, 'GetProperties',
(['out'], ctypes.POINTER(VDS_VOLUME_PROP),
'pVolumeProperties')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'GetPack',
(['out'], ctypes.POINTER(ctypes.POINTER(IVdsPack)),
'ppPack')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'QueryPlexes',
(['out'],
ctypes.POINTER(ctypes.POINTER(IEnumVdsObject)),
'ppEnum')),
comtypes.COMMETHOD([], comtypes.HRESULT, 'Extend',
(['in'], ctypes.POINTER(
VDS_INPUT_DISK), 'pInputDiskArray'),
(['in'], wintypes.LONG, 'lNumberOfDisks'),
(['out'], ctypes.POINTER(
ctypes.POINTER(IVdsAsync)), 'ppAsync'),
),
comtypes.COMMETHOD([], comtypes.HRESULT, 'Shrink',
(['in'], wintypes.ULARGE_INTEGER,
'ullNumberOfBytesToRemove'),
(['out'], ctypes.POINTER(ctypes.POINTER(IVdsAsync)),
'ppAsync')),
]
def load_vds_service():
loader = client.CreateObject(CLSID_VdsLoader, interface=IVdsServiceLoader)
svc = loader.LoadService(None)
svc.WaitForServiceReady()
return svc