Fixes https://trello.com/c/8KGzMa75 - Updates to VMware inventory script to include additional host variables, groups and ansible_ssh_host.

This commit is contained in:
Chris Church
2014-10-30 13:47:38 -04:00
parent 202b511f55
commit f5a174f991
5 changed files with 417 additions and 174 deletions

View File

@@ -956,7 +956,7 @@ class RunInventoryUpdate(BaseTask):
ec2_opts['cache_path'] = cache_path ec2_opts['cache_path'] = cache_path
ec2_opts.setdefault('cache_max_age', '300') ec2_opts.setdefault('cache_max_age', '300')
for k,v in ec2_opts.items(): for k,v in ec2_opts.items():
cp.set(section, k, str(v)) cp.set(section, k, unicode(v))
# Build pyrax creds INI for rax inventory script. # Build pyrax creds INI for rax inventory script.
elif inventory_update.source == 'rax': elif inventory_update.source == 'rax':
section = 'rackspace_cloud' section = 'rackspace_cloud'
@@ -966,6 +966,15 @@ class RunInventoryUpdate(BaseTask):
cp.set(section, 'username', credential.username) cp.set(section, 'username', credential.username)
cp.set(section, 'api_key', decrypt_field(credential, cp.set(section, 'api_key', decrypt_field(credential,
'password')) 'password'))
# Allow custom options to vmware inventory script.
elif inventory_update.source == 'vmware':
section = 'defaults'
cp.add_section(section)
vmware_opts = dict(inventory_update.source_vars_dict.items())
vmware_opts.setdefault('guests_only', 'True')
for k,v in vmware_opts.items():
cp.set(section, k, unicode(v))
# Return INI content. # Return INI content.
if cp.sections(): if cp.sections():
f = cStringIO.StringIO() f = cStringIO.StringIO()
@@ -1026,6 +1035,7 @@ class RunInventoryUpdate(BaseTask):
# complain about not being able to determine its version number. # complain about not being able to determine its version number.
env['PBR_VERSION'] = '0.5.21' env['PBR_VERSION'] = '0.5.21'
elif inventory_update.source == 'vmware': elif inventory_update.source == 'vmware':
env['VMWARE_INI'] = kwargs.get('private_data_file', '')
env['VMWARE_HOST'] = passwords.get('source_host', '') env['VMWARE_HOST'] = passwords.get('source_host', '')
env['VMWARE_USER'] = passwords.get('source_username', '') env['VMWARE_USER'] = passwords.get('source_username', '')
env['VMWARE_PASSWORD'] = passwords.get('source_password', '') env['VMWARE_PASSWORD'] = passwords.get('source_password', '')

View File

@@ -1562,6 +1562,62 @@ class InventoryUpdatesTest(BaseTransactionTest):
# its own child). # its own child).
self.assertTrue(self.group in self.inventory.root_groups) self.assertTrue(self.group in self.inventory.root_groups)
def test_update_from_vmware(self):
source_host = getattr(settings, 'TEST_VMWARE_HOST', '')
source_username = getattr(settings, 'TEST_VMWARE_USER', '')
source_password = getattr(settings, 'TEST_VMWARE_PASSWORD', '')
if not all([source_host, source_username, source_password]):
self.skipTest('no test vmware credentials defined!')
self.create_test_license_file()
credential = Credential.objects.create(kind='vmware',
user=self.super_django_user,
username=source_username,
password=source_password,
host=source_host)
inventory_source = self.update_inventory_source(self.group,
source='vmware', credential=credential)
# Check first without instance_id set (to import by name only).
with self.settings(VMWARE_INSTANCE_ID_VAR=''):
self.check_inventory_source(inventory_source)
# Rename hosts and verify the import picks up the instance_id present
# in host variables.
for host in self.inventory.hosts.all():
self.assertFalse(host.instance_id, host.instance_id)
if host.enabled:
self.assertTrue(host.variables_dict.get('ansible_ssh_host', ''))
# Test a field that should be present for host systems, not VMs.
self.assertFalse(host.variables_dict.get('vmware_product_name', ''))
host.name = 'updated-%s' % host.name
host.save()
old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True))
self.check_inventory_source(inventory_source, initial=False)
new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True))
self.assertEqual(old_host_pks, new_host_pks)
# Manually disable all hosts, verify a new update re-enables them.
# Also change the host name, and verify it is not deleted, but instead
# updated because the instance ID matches.
enabled_host_pks = set(self.inventory.hosts.filter(enabled=True).values_list('pk', flat=True))
for host in self.inventory.hosts.all():
host.enabled = False
host.name = 'changed-%s' % host.name
host.save()
old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True))
self.check_inventory_source(inventory_source, initial=False, enabled_host_pks=enabled_host_pks)
new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True))
self.assertEqual(old_host_pks, new_host_pks)
# Update again and include host systems in addition to guests.
inventory_source.source_vars = '---\n\nguests_only: false\n'
inventory_source.save()
old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True))
self.check_inventory_source(inventory_source, initial=False)
new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True))
self.assertTrue(new_host_pks > old_host_pks)
for host in self.inventory.hosts.filter(pk__in=(new_host_pks - old_host_pks)):
if host.enabled:
self.assertTrue(host.variables_dict.get('ansible_ssh_host', ''))
# Test a field only present for host systems.
self.assertTrue(host.variables_dict.get('vmware_product_name', ''))
def test_update_from_custom_script(self): def test_update_from_custom_script(self):
# Create the inventory script # Create the inventory script
self.create_test_license_file() self.create_test_license_file()

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
VMWARE external inventory script VMware Inventory Script
================================= =======================
shamelessly copied from existing inventory scripts. shamelessly copied from existing inventory scripts.
@@ -14,219 +14,391 @@ i.e vmware.py/vmware_colo.ini vmware_idf.py/vmware_idf.ini
so if you don't have clustered vcenter but multiple esx machines or so if you don't have clustered vcenter but multiple esx machines or
just diff clusters you can have a inventory per each and automatically just diff clusters you can have a inventory per each and automatically
group hosts based on file name or specify a group in the ini. group hosts based on file name or specify a group in the ini.
FIXME
''' '''
import collections
import json
import logging
import optparse
import os import os
import sys import sys
import time import time
import ConfigParser import ConfigParser
from psphere.client import Client
from psphere.managedobjects import HostSystem
# Disable logging message trigged by pSphere/suds.
try: try:
import json from logging import NullHandler
except ImportError: except ImportError:
import simplejson as json from logging import Handler
class NullHandler(Handler):
def emit(self, record):
pass
logging.getLogger('psphere').addHandler(NullHandler())
logging.getLogger('suds').addHandler(NullHandler())
from psphere.client import Client
from psphere.errors import ObjectNotFoundError
from psphere.managedobjects import HostSystem, VirtualMachine, ManagedObject, Network
from suds.sudsobject import Object as SudsObject
def save_cache(cache_item, data, config): class VMwareInventory(object):
''' saves item to cache '''
def __init__(self, guests_only=None):
self.config = ConfigParser.SafeConfigParser()
if os.environ.get('VMWARE_INI', ''):
config_files = [os.environ['VMWARE_INI']]
else:
config_files = [os.path.abspath(sys.argv[0]).rstrip('.py') + '.ini', 'vmware.ini']
for config_file in config_files:
if os.path.exists(config_file):
self.config.read(config_file)
break
# Sanity check: Is caching enabled? If not, don't cache. # Retrieve only guest VMs, or include host systems?
if not config.has_option('defaults', 'cache_dir'): if guests_only is not None:
return self.guests_only = guests_only
elif self.config.has_option('defaults', 'guests_only'):
self.guests_only = self.config.getboolean('defaults', 'guests_only')
else:
self.guests_only = True
dpath = config.get('defaults', 'cache_dir') # Read authentication information from VMware environment variables
try: # (if set), otherwise from INI file.
cache = open('/'.join([dpath,cache_item]), 'w') auth_host = os.environ.get('VMWARE_HOST')
cache.write(json.dumps(data)) if not auth_host and self.config.has_option('auth', 'host'):
cache.close() auth_host = self.config.get('auth', 'host')
except IOError, e: auth_user = os.environ.get('VMWARE_USER')
pass # not really sure what to do here if not auth_user and self.config.has_option('auth', 'user'):
auth_user = self.config.get('auth', 'user')
auth_password = os.environ.get('VMWARE_PASSWORD')
if not auth_password and self.config.has_option('auth', 'password'):
auth_password = self.config.get('auth', 'password')
# Create the VMware client connection.
self.client = Client(auth_host, auth_user, auth_password)
def get_cache(cache_item, config): def _put_cache(self, name, value):
''' returns cached item ''' '''
Saves the value to cache with the name given.
'''
if self.config.has_option('defaults', 'cache_dir'):
cache_dir = self.config.get('defaults', 'cache_dir')
if not os.path.exists(cache_dir):
os.makedirs(cache_dir)
cache_file = os.path.join(cache_dir, name)
with open(cache_file, 'w') as cache:
json.dump(value, cache)
# Sanity check: Is caching enabled? If not, return None. def _get_cache(self, name, default=None):
if not config.has_option('defaults', 'cache_dir'): '''
return Retrieves the value from cache for the given name.
'''
if self.config.has_option('defaults', 'cache_dir'):
cache_dir = self.config.get('defaults', 'cache_dir')
cache_file = os.path.join(cache_dir, name)
if os.path.exists(cache_file):
if self.config.has_option('defaults', 'cache_max_age'):
cache_max_age = self.config.getint('defaults', 'cache_max_age')
else:
cache_max_age = 0
cache_stat = os.stat(cache_file)
if (cache_stat.st_mtime + cache_max_age) < time.time():
with open(cache_file) as cache:
return json.load(cache)
return default
dpath = config.get('defaults', 'cache_dir') def _flatten_dict(self, d, parent_key='', sep='_'):
inv = {} '''
try: Flatten nested dicts by combining keys with a separator. Lists with
cache = open('/'.join([dpath,cache_item]), 'r') only string items are included as is; any other lists are discarded.
inv = json.loads(cache.read()) '''
cache.close() items = []
except IOError, e: for k, v in d.items():
pass # not really sure what to do here if k.startswith('_'):
continue
new_key = parent_key + sep + k if parent_key else k
if isinstance(v, collections.MutableMapping):
items.extend(self._flatten_dict(v, new_key, sep).items())
elif isinstance(v, (list, tuple)):
if all([isinstance(x, basestring) for x in v]):
items.append((new_key, v))
else:
items.append((new_key, v))
return dict(items)
return inv def _get_obj_info(self, obj, depth=99, seen=None):
'''
def cache_available(cache_item, config): Recursively build a data structure for the given pSphere object (depth
''' checks if we have a 'fresh' cache available for item requested ''' only applies to ManagedObject instances).
'''
if config.has_option('defaults', 'cache_dir'): seen = seen or set()
dpath = config.get('defaults', 'cache_dir') if isinstance(obj, ManagedObject):
try:
obj_unicode = unicode(getattr(obj, 'name'))
except AttributeError:
obj_unicode = ()
if obj in seen:
return obj_unicode
seen.add(obj)
if depth <= 0:
return obj_unicode
d = {}
for attr in dir(obj):
if attr.startswith('_'):
continue
try:
val = getattr(obj, attr)
obj_info = self._get_obj_info(val, depth - 1, seen)
if obj_info != ():
d[attr] = obj_info
except Exception, e:
pass
return d
elif isinstance(obj, SudsObject):
d = {}
for key, val in iter(obj):
obj_info = self._get_obj_info(val, depth, seen)
if obj_info != ():
d[key] = obj_info
return d
elif isinstance(obj, (list, tuple)):
l = []
for val in iter(obj):
obj_info = self._get_obj_info(val, depth, seen)
if obj_info != ():
l.append(obj_info)
return l
elif isinstance(obj, (type(None), bool, int, long, float, basestring)):
return obj
else:
return ()
def _get_host_info(self, host, prefix='vmware'):
'''
Return a flattened dict with info about the given host system.
'''
host_info = {
'name': host.name,
'tag': host.tag,
'datastores': self._get_obj_info(host.datastore, depth=0),
'networks': self._get_obj_info(host.network, depth=0),
'vms': self._get_obj_info(host.vm, depth=0),
}
for k, v in self._get_obj_info(host.summary, depth=0).items():
if isinstance(v, collections.MutableMapping):
for k2, v2 in v.items():
host_info[k2] = v2
elif k != 'host':
host_info[k] = v
try: try:
existing = os.stat( '/'.join([dpath,cache_item])) host_info['ipAddress'] = host.config.network.vnic[0].spec.ip.ipAddress
except: except Exception, e:
# cache doesn't exist or isn't accessible print >> sys.stderr, e
return False host_info = self._flatten_dict(host_info, prefix)
if ('%s_ipAddress' % prefix) in host_info:
host_info['ansible_ssh_host'] = host_info['%s_ipAddress' % prefix]
return host_info
if config.has_option('defaults', 'cache_max_age'): def _get_vm_info(self, vm, prefix='vmware'):
maxage = config.get('defaults', 'cache_max_age') '''
Return a flattened dict with info about the given virtual machine.
'''
vm_info = {
'name': vm.name,
'tag': vm.tag,
'datastores': self._get_obj_info(vm.datastore, depth=0),
'networks': self._get_obj_info(vm.network, depth=0),
'resourcePool': self._get_obj_info(vm.resourcePool, depth=0),
'guestState': vm.guest.guestState,
}
for k, v in self._get_obj_info(vm.summary, depth=0).items():
if isinstance(v, collections.MutableMapping):
for k2, v2 in v.items():
if k2 == 'host':
k2 = 'hostSystem'
vm_info[k2] = v2
elif k != 'vm':
vm_info[k] = v
vm_info = self._flatten_dict(vm_info, prefix)
if ('%s_ipAddress' % prefix) in vm_info:
vm_info['ansible_ssh_host'] = vm_info['%s_ipAddress' % prefix]
return vm_info
if (existing.st_mtime - int(time.time())) <= maxage: def _add_host(self, inv, parent_group, host_name):
return True '''
Add the host to the parent group in the given inventory.
'''
p_group = inv.setdefault(parent_group, [])
if isinstance(p_group, dict):
group_hosts = p_group.setdefault('hosts', [])
else:
group_hosts = p_group
if host_name not in group_hosts:
group_hosts.append(host_name)
return False def _add_child(self, inv, parent_group, child_group):
'''
Add a child group to a parent group in the given inventory.
'''
if parent_group != 'all':
p_group = inv.setdefault(parent_group, {})
if not isinstance(p_group, dict):
inv[parent_group] = {'hosts': p_group}
p_group = inv[parent_group]
group_children = p_group.setdefault('children', [])
if child_group not in group_children:
group_children.append(child_group)
inv.setdefault(child_group, [])
def get_host_info(host): def get_inventory(self, meta_hostvars=True):
''' Get variables about a specific host ''' '''
Reads the inventory from cache or VMware API via pSphere.
'''
# Use different cache names for guests only vs. all hosts.
if self.guests_only:
cache_name = '__inventory_guests__'
else:
cache_name = '__inventory_all__'
hostinfo = { inv = self._get_cache(cache_name, None)
'vmware_name' : host.name, if inv is not None:
'vmware_tag' : host.tag, return inv
'vmware_parent': host.parent.name,
}
for k in host.capability.__dict__.keys():
if k.startswith('_'):
continue
try:
hostinfo['vmware_' + k] = str(host.capability[k])
except:
continue
return hostinfo inv = {'all': {'hosts': []}}
if meta_hostvars:
inv['_meta'] = {'hostvars': {}}
def get_inventory(client, config):
''' Reads the inventory from cache or vmware api '''
if cache_available('inventory', config):
inv = get_cache('inventory',config)
else:
inv= { 'all': {'hosts': []}, '_meta': { 'hostvars': {} } }
default_group = os.path.basename(sys.argv[0]).rstrip('.py') default_group = os.path.basename(sys.argv[0]).rstrip('.py')
if config.has_option('defaults', 'guests_only'): if not self.guests_only:
guests_only = config.get('defaults', 'guests_only') if self.config.has_option('defaults', 'hw_group'):
else: hw_group = self.config.get('defaults', 'hw_group')
guests_only = True
if not guests_only:
if config.has_option('defaults','hw_group'):
hw_group = config.get('defaults','hw_group')
else: else:
hw_group = default_group + '_hw' hw_group = default_group + '_hw'
inv[hw_group] = []
if config.has_option('defaults','vm_group'): if self.config.has_option('defaults', 'vm_group'):
vm_group = config.get('defaults','vm_group') vm_group = self.config.get('defaults', 'vm_group')
else: else:
vm_group = default_group + '_vm' vm_group = default_group + '_vm'
inv[vm_group] = []
# Loop through physical hosts: # Loop through physical hosts:
hosts = HostSystem.all(client) for host in HostSystem.all(self.client):
for host in hosts:
if not guests_only:
inv['all']['hosts'].append(host.name)
inv[hw_group].append(host.name)
if host.tag:
taggroup = 'vmware_' + host.tag
if taggroup in inv:
inv[taggroup].append(host.name)
else:
inv[taggroup] = [ host.name ]
inv['_meta']['hostvars'][host.name] = get_host_info(host) if not self.guests_only:
save_cache(vm.name, inv['_meta']['hostvars'][host.name], config) self._add_host(inv, 'all', host.name)
self._add_host(inv, hw_group, host.name)
if host.tag: # FIXME: Is this always a string?
host_tag = 'vmware_%s' % host.tag
self._add_host(inv, host_tag, host.name)
host_info = self._get_host_info(host)
if meta_hostvars:
inv['_meta']['hostvars'][host.name] = host_info
self._put_cache(host.name, host_info)
# Loop through all VMs on physical host.
for vm in host.vm: for vm in host.vm:
inv['all']['hosts'].append(vm.name) self._add_host(inv, 'all', vm.name)
inv[vm_group].append(vm.name) self._add_host(inv, vm_group, vm.name)
for tag in vm.tag: if vm.tag: # FIXME: Is this always a string?
taggroup = 'vmware_' + tag.key.lower() vm_tag = 'vmware_%s' % vm.tag
if taggroup in inv: self._add_host(inv, vm_tag, vm.name)
inv[taggroup].append(vm.name) vm_info = self._get_vm_info(vm)
else: if meta_hostvars:
inv[taggroup] = [ vm.name ] inv['_meta']['hostvars'][vm.name] = vm_info
self._put_cache(vm.name, vm_info)
inv['_meta']['hostvars'][vm.name] = get_host_info(host) # Group by resource pool.
save_cache(vm.name, inv['_meta']['hostvars'][vm.name], config) vm_resourcePool = vm_info.get('vmware_resourcePool', None)
if vm_resourcePool:
self._add_child(inv, vm_group, 'resource_pools')
self._add_child(inv, 'resource_pools', vm_resourcePool)
self._add_host(inv, vm_resourcePool, vm.name)
save_cache('inventory', inv, config) # Group by datastore.
return json.dumps(inv) for vm_datastore in vm_info.get('vmware_datastores', []):
self._add_child(inv, vm_group, 'datastores')
self._add_child(inv, 'datastores', vm_datastore)
self._add_host(inv, vm_datastore, vm.name)
def get_single_host(client, config, hostname): # Group by network.
for vm_network in vm_info.get('vmware_networks', []):
self._add_child(inv, vm_group, 'networks')
self._add_child(inv, 'networks', vm_network)
self._add_host(inv, vm_network, vm.name)
inv = {} # Group by guest OS.
vm_guestId = vm_info.get('vmware_guestId', None)
if vm_guestId:
self._add_child(inv, vm_group, 'guests')
self._add_child(inv, 'guests', vm_guestId)
self._add_host(inv, vm_guestId, vm.name)
if cache_available(hostname, config): self._put_cache(cache_name, inv)
inv = get_cache(hostname,config) return inv
def get_host(self, hostname):
'''
Read info about a specific host or VM from cache or VMware API.
'''
inv = self._get_cache(hostname, None)
if inv is not None:
return inv
if not self.guests_only:
try:
host = HostSystem.get(self.client, name=hostname)
inv = self._get_host_info(host)
except ObjectNotFoundError:
pass
if inv is None:
try:
vm = VirtualMachine.get(self.client, name=hostname)
inv = self._get_vm_info(vm)
except ObjectNotFoundError:
pass
if inv is not None:
self._put_cache(hostname, inv)
return inv or {}
def main():
parser = optparse.OptionParser()
parser.add_option('--list', action='store_true', dest='list',
default=False, help='Output inventory groups and hosts')
parser.add_option('--host', dest='host', default=None, metavar='HOST',
help='Output variables only for the given hostname')
# Additional options for use when running the script standalone, but never
# used by Ansible.
parser.add_option('--pretty', action='store_true', dest='pretty',
default=False, help='Output nicely-formatted JSON')
parser.add_option('--include-host-systems', action='store_true',
dest='include_host_systems', default=False,
help='Include host systems in addition to VMs')
parser.add_option('--no-meta-hostvars', action='store_false',
dest='meta_hostvars', default=True,
help='Exclude [\'_meta\'][\'hostvars\'] with --list')
options, args = parser.parse_args()
if options.include_host_systems:
vmware_inventory = VMwareInventory(guests_only=False)
else: else:
hosts = HostSystem.all(client) #TODO: figure out single host getter vmware_inventory = VMwareInventory()
for host in hosts: if options.host is not None:
if hostname == host.name: inventory = vmware_inventory.get_host(options.host)
inv = get_host_info(host) else:
break inventory = vmware_inventory.get_inventory(options.meta_hostvars)
for vm in host.vm:
if hostname == vm.name: json_kwargs = {}
inv = get_host_info(host) if options.pretty:
break json_kwargs.update({'indent': 4, 'sort_keys': True})
save_cache(hostname,inv,config) json.dump(inventory, sys.stdout, **json_kwargs)
return json.dumps(inv)
if __name__ == '__main__': if __name__ == '__main__':
inventory = {} main()
hostname = None
if len(sys.argv) > 1:
if sys.argv[1] == "--host":
hostname = sys.argv[2]
# Read config
config = ConfigParser.SafeConfigParser(
defaults={'host': '', 'user': '', 'password': ''},
)
for section in ('auth', 'defaults'):
config.add_section(section)
for configfilename in [os.path.abspath(sys.argv[0]).rstrip('.py') + '.ini', 'vmware.ini']:
if os.path.exists(configfilename):
config.read(configfilename)
break
auth_host, auth_user, auth_password = None, None, None
# Read our authentication information from the INI file, if it exists.
try:
auth_host = config.get('auth', 'host')
auth_user = config.get('auth', 'user')
auth_password = config.get('auth', 'password')
except Exception:
pass
# If any of the VMware environment variables are set, they trump
# the INI configuration.
if 'VMWARE_HOST' in os.environ:
auth_host = os.environ['VMWARE_HOST']
if 'VMWARE_USER' in os.environ:
auth_user = os.environ['VMWARE_USER']
if 'VMWARE_PASSWORD' in os.environ:
auth_password = os.environ['VMWARE_PASSWORD']
# Create the VMware client.
client = Client(auth_host, auth_user, auth_password)
# Actually do the work.
if hostname is None:
inventory = get_inventory(client, config)
else:
inventory = get_single_host(client, config, hostname)
# Return to Ansible.
print inventory

View File

@@ -399,11 +399,11 @@ VMWARE_REGIONS_BLACKLIST = []
# Inventory variable name/values for determining whether a host is # Inventory variable name/values for determining whether a host is
# active in vSphere. # active in vSphere.
VMWARE_ENABLED_VAR = 'status' VMWARE_ENABLED_VAR = 'vmware_powerState'
VMWARE_ENABLED_VALUE = 'POWERED ON' VMWARE_ENABLED_VALUE = 'poweredOn'
# Inventory variable name containing the unique instance ID. # Inventory variable name containing the unique instance ID.
VMWARE_INSTANCE_ID_VAR = 'guest_id' VMWARE_INSTANCE_ID_VAR = 'vmware_uuid'
# Filter for allowed group and host names when importing inventory # Filter for allowed group and host names when importing inventory
# from EC2. # from EC2.

View File

@@ -484,3 +484,8 @@ TEST_AWS_REGIONS = 'all'
TEST_RACKSPACE_USERNAME = '' TEST_RACKSPACE_USERNAME = ''
TEST_RACKSPACE_API_KEY = '' TEST_RACKSPACE_API_KEY = ''
TEST_RACKSPACE_REGIONS = 'all' TEST_RACKSPACE_REGIONS = 'all'
# VMware credentials
TEST_VMWARE_HOST = ''
TEST_VMWARE_USER = ''
TEST_VMWARE_PASSWORD = ''