Skip to content
This repository has been archived by the owner on Nov 9, 2020. It is now read-only.

Commit

Permalink
Place dockvols to datastore root. Also locate VM by uuid rather than …
Browse files Browse the repository at this point in the history
…by Name (#451)

Place dockvols to datastore root - Fixe #449.
Also , if VM as renamed, the whole "find VM" mechanism was confused - so I fixed by by using search by UUID rather than
scan for name. It has a side effect of being faster (on 10 VMs query time dropped from 0.25 sec to 0.05 sec)
  • Loading branch information
Mark Sterin committed Jun 11, 2016
1 parent 6d7f217 commit 9b32648
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 50 deletions.
4 changes: 3 additions & 1 deletion esx_service/utils/vsan_info_test.py
Expand Up @@ -49,7 +49,9 @@ def setUp(self):
"""create a vmdk before each test (method) in this class"""
self.si = vmdk_ops.connectLocal()
# create VMDK
err = vmdk_ops.createVMDK(self.VM_NAME, self.VMDK_PATH, "test_policy_vol")
err = vmdk_ops.createVMDK(vmdk_path=self.VMDK_PATH,
vm_name=self.VM_NAME,
vol_name="test_policy_vol")
self.assertEqual(err, None, err)

def tearDown(self):
Expand Down
97 changes: 48 additions & 49 deletions esx_service/vmdk_ops.py
Expand Up @@ -48,7 +48,6 @@

import pyVim
from pyVim.connect import Connect, Disconnect
from pyVim.invt import GetVmFolder, FindChild
from pyVim import vmconfig

from pyVmomi import VmomiSupport, vim, vmodl
Expand Down Expand Up @@ -117,7 +116,7 @@ def RunCommand(cmd):
# returns error, or None for OK
# opts is dictionary of {option: value}.
# for now we care about size and (maybe) policy
def createVMDK(vm_name, vmdk_path, vol_name, opts={}):
def createVMDK(vmdk_path, vm_name, vol_name, opts={}):
logging.info("*** createVMDK: %s opts = %s", vmdk_path, opts)
if os.path.isfile(vmdk_path):
return err("File %s already exists" % vmdk_path)
Expand Down Expand Up @@ -271,7 +270,7 @@ def formatVmdk(vmdk_path, vol_name):
return None


#returns error, or None for OK
# Return error, or None for OK
def removeVMDK(vmdk_path):
logging.info("*** removeVMDK: %s", vmdk_path)
cmd = "{0} {1}".format(VMDK_DELETE_CMD, vmdk_path)
Expand All @@ -290,51 +289,47 @@ def listVMDK(path):
u'Attributes': {}} for x in vmdks]


# Find VM , reconnect if needed. throws on error
def findVmByName(vm_name):
vm = None
vm, _ = find_child(vm_name)
if not vm:
# try again
connectLocal()
vm, e = find_child(vm_name)

if not vm:
logging.error("VM {0} not found".format(vm_name))
raise e

return vm

def find_child(vm_name):
e = None
# Return VM managed object, reconnect if needed. Throws if fails twice.
def findVmByUuid(vm_uuid):
try:
vm = FindChild(GetVmFolder(), vm_name)
vm = si.content.searchIndex.FindByUuid(None, vm_uuid, True, False)
if vm:
return vm
except Exception as e:
logging.exception("Failed to find vm: %s", vm_name)
vm = None
return vm, e
logging.exception("Failed to find VM uuid=%s (traceback below), " \
"retrying...", vm_uuid)
#
# Retry. It can throw if connect/search fails. But search can't return None
# since we get UUID from VMM so VM must exist
#
connectLocal()
vm = si.content.searchIndex.FindByUuid(None, vm_uuid, True, False)
logging.info("Found VM name=%s, id=%s ", vm.config.name, vm_uuid)
return vm

#returns error, or None for OK
def attachVMDK(vmdk_path, vm_name):
vm = findVmByName(vm_name)

# Return error, or None for OK.
def attachVMDK(vmdk_path, vm_uuid):
vm = findVmByUuid(vm_uuid)
logging.info("*** attachVMDK: %s to %s VM uuid = %s",
vmdk_path, vm_name, vm.config.uuid)
vmdk_path, vm.config.name, vm_uuid)
return disk_attach(vmdk_path, vm)


#returns error, or None for OK
def detachVMDK(vmdk_path, vm_name):
vm = findVmByName(vm_name)
# Return error, or None for OK.
def detachVMDK(vmdk_path, vm_uuid):
vm = findVmByUuid(vm_uuid)
logging.info("*** detachVMDK: %s from %s VM uuid = %s",
vmdk_path, vm_name, vm.config.uuid)
vmdk_path, vm.config.name, vm_uuid)
return disk_detach(vmdk_path, vm)


# Check existence (and creates if needed) the path
def getVolPath(vm_config_path):
# The volumes folder is created in the parent of the given VM's folder.
path = os.path.join(
os.path.dirname(os.path.dirname(vm_config_path)), DOCK_VOLS_DIR)
# The folder for Docker volumes is created on <vm_datastore>/DOCK_VOLS_DIR
# On ESX the path is always /vmfs/volume/datastore/... , so we can do this:
vm_datastore = "/".join(vm_config_path.split("/")[0:4])
path = os.path.join(vm_datastore, DOCK_VOLS_DIR)

if os.path.isdir(path):
# If the path exists then return it as is.
Expand All @@ -359,7 +354,7 @@ def getVmdkName(path, vol_name):


# gets the requests, calculates path for volumes, and calls the relevant handler
def executeRequest(vm_name, config_path, cmd, vol_name, opts):
def executeRequest(vm_uuid, vm_name, config_path, cmd, vol_name, opts):
# get /vmfs/volumes/<volid> path on ESX:
path = getVolPath(config_path)

Expand All @@ -369,15 +364,15 @@ def executeRequest(vm_name, config_path, cmd, vol_name, opts):
vmdk_path = getVmdkName(path, vol_name)

if cmd == "create":
response = createVMDK(vm_name, vmdk_path, vol_name, opts)
response = createVMDK(vmdk_path, vm_name, vol_name, opts)
elif cmd == "remove":
response = removeVMDK(vmdk_path)
elif cmd == "list":
response = listVMDK(path)
elif cmd == "attach":
response = attachVMDK(vmdk_path, vm_name)
response = attachVMDK(vmdk_path, vm_uuid)
elif cmd == "detach":
response = detachVMDK(vmdk_path, vm_name)
response = detachVMDK(vmdk_path, vm_uuid)
else:
return err("Unknown command:" + cmd)

Expand Down Expand Up @@ -405,23 +400,22 @@ def connectLocal():


def findDeviceByPath(vmdk_path, vm):

for d in vm.config.hardware.device:
if type(d) != vim.vm.device.VirtualDisk:
continue

# Disks of all backing have a backing object with
# a filename attribute in it. The filename identifies the
# virtual disk by name and can be used to try a match
# with the given name. Filename has format like,
# "[<datastore name>] <parent-directory>/<vmdk-descriptor-name>".
# Disks of all backing have a backing object with a filename attribute.
# The filename identifies the virtual disk by name and can be used
# to match with the given name.
# Filename format is as follows:
# "[<datastore name>] <parent-directory>/<vmdk-descriptor-name>"
backing_disk = d.backing.fileName.split(" ")[1]

# Construct the parent dir and vmdk name, resolving
# links if any.
dvol_dir = os.path.dirname(vmdk_path)
real_vol_dir = os.path.basename(os.path.realpath(dvol_dir))
virtual_disk = real_vol_dir + "/" + os.path.basename(vmdk_path)
virtual_disk = os.path.join(real_vol_dir, os.path.basename(vmdk_path))
if virtual_disk == backing_disk:
logging.debug("findDeviceByPath: MATCH: %s", backing_disk)
return d
Expand Down Expand Up @@ -605,8 +599,8 @@ def disk_attach(vmdk_path, vm):
return err(msg)

setStatusAttached(vmdk_path, vm)
logging.info("Disk %s successfully attached. disk_slot = %d, bus_number = %d",
vmdk_path, disk_slot, bus_number)
logging.info("Disk %s successfully attached. bus_number=%d, disk_slot=%d",
vmdk_path, bus_number, disk_slot)
return busInfo(disk_slot, bus_number)


Expand Down Expand Up @@ -695,6 +689,11 @@ def handleVmciRequests(port):

vm_name = group_info["displayName"]
cfg_path = group_info["cfgPath"]
uuid = group_info["uuid"]
# pyVmomi expects uuid like this one: 564dac12-b1a0-f735-0df3-bceb00b30340
# to get it from uuid in VSI vms/<id>/vmmGroup, we use the following format:
UUID_FORMAT = "{0}{1}{2}{3}-{4}{5}-{6}{7}-{8}{9}-{10}{11}{12}{13}{14}{15}"
vm_uuid = UUID_FORMAT.format(*uuid.replace("-", " ").split())

try:
req = json.loads(txt.value, "utf-8")
Expand All @@ -704,7 +703,7 @@ def handleVmciRequests(port):
details = req["details"]
opts = details["Opts"] if "Opts" in details else {}
threading.currentThread().setName(vm_name)
ret = executeRequest(vm_name, cfg_path, req["cmd"],
ret = executeRequest(vm_uuid, vm_name, cfg_path, req["cmd"],
details["Name"], opts)
logging.info("executeRequest '%s' completed with ret=%s",
req["cmd"], ret)
Expand Down

0 comments on commit 9b32648

Please sign in to comment.