diff --git a/etc/faafo.conf b/etc/faafo.conf index 4cf88bb..1d2a49b 100644 --- a/etc/faafo.conf +++ b/etc/faafo.conf @@ -173,310 +173,6 @@ transport_url = rabbit://guest:guest@localhost:5672/ #control_exchange = openstack -[glance_store] - -# -# From glance.store -# - -# List of stores enabled (list value) -stores = file - -# Default scheme to use to store image data. The scheme must be -# registered by one of the stores defined by the 'stores' config -# option. (string value) -default_store = file - -# Minimum interval seconds to execute updating dynamic storage -# capabilities based on backend status then. It's not a periodic -# routine, the update logic will be executed only when interval -# seconds elapsed and an operation of store has triggered. The feature -# will be enabled only when the option value greater then zero. -# (integer value) -#store_capabilities_update_min_interval = 0 - -# -# From glance.store -# - -# Directory to which the Filesystem backend store writes images. -# (string value) -# Deprecated group/name - [DEFAULT]/filesystem_store_datadir -filesystem_store_datadir = /tmp - -# List of directories and its priorities to which the Filesystem -# backend store writes images. (multi valued) -# Deprecated group/name - [DEFAULT]/filesystem_store_datadirs -#filesystem_store_datadirs = - -# The path to a file which contains the metadata to be returned with -# any location associated with this store. The file must contain a -# valid JSON object. The object should contain the keys 'id' and -# 'mountpoint'. The value for both keys should be 'string'. (string -# value) -# Deprecated group/name - [DEFAULT]/filesystem_store_metadata_file -#filesystem_store_metadata_file = - -# The required permission for created image file. In this way the user -# other service used, e.g. Nova, who consumes the image could be the -# exclusive member of the group that owns the files created. Assigning -# it less then or equal to zero means don't change the default -# permission of the file. This value will be decoded as an octal -# digit. (integer value) -# Deprecated group/name - [DEFAULT]/filesystem_store_file_perm -#filesystem_store_file_perm = 0 - -# RADOS images will be chunked into objects of this size (in -# megabytes). For best performance, this should be a power of two. -# (integer value) -#rbd_store_chunk_size = 8 - -# RADOS pool in which images are stored. (string value) -#rbd_store_pool = images - -# RADOS user to authenticate as (only applicable if using Cephx. If -# , a default will be chosen based on the client. section in -# rbd_store_ceph_conf) (string value) -#rbd_store_user = - -# Ceph configuration file path. If , librados will locate the -# default config. If using cephx authentication, this file should -# include a reference to the right keyring in a client. section -# (string value) -#rbd_store_ceph_conf = /etc/ceph/ceph.conf - -# The host where the S3 server is listening. (string value) -#s3_store_host = - -# The S3 query token access key. (string value) -#s3_store_access_key = - -# The S3 query token secret key. (string value) -#s3_store_secret_key = - -# The S3 bucket to be used to store the Glance data. (string value) -#s3_store_bucket = - -# The local directory where uploads will be staged before they are -# transferred into S3. (string value) -#s3_store_object_buffer_dir = - -# A boolean to determine if the S3 bucket should be created on upload -# if it does not exist or if an error should be returned to the user. -# (boolean value) -#s3_store_create_bucket_on_put = false - -# The S3 calling format used to determine the bucket. Either subdomain -# or path can be used. (string value) -#s3_store_bucket_url_format = subdomain - -# What size, in MB, should S3 start chunking image files and do a -# multipart upload in S3. (integer value) -#s3_store_large_object_size = 100 - -# What multipart upload part size, in MB, should S3 use when uploading -# parts. The size must be greater than or equal to 5M. (integer value) -#s3_store_large_object_chunk_size = 10 - -# The number of thread pools to perform a multipart upload in S3. -# (integer value) -#s3_store_thread_pools = 10 - -# Images will be chunked into objects of this size (in megabytes). For -# best performance, this should be a power of two. (integer value) -#sheepdog_store_chunk_size = 64 - -# Port of sheep daemon. (integer value) -#sheepdog_store_port = 7000 - -# IP address of sheep daemon. (string value) -#sheepdog_store_address = localhost - -# ESX/ESXi or vCenter Server target system. The server value can be an -# IP address or a DNS name. (string value) -#vmware_server_host = - -# Username for authenticating with VMware ESX/VC server. (string -# value) -#vmware_server_username = - -# Password for authenticating with VMware ESX/VC server. (string -# value) -#vmware_server_password = - -# DEPRECATED. Inventory path to a datacenter. If the -# vmware_server_host specified is an ESX/ESXi, the -# vmware_datacenter_path is optional. If specified, it should be "ha- -# datacenter". This option is deprecated in favor of vmware_datastores -# and will be removed in the Liberty release. (string value) -#vmware_datacenter_path = ha-datacenter - -# DEPRECATED. Datastore associated with the datacenter. This option is -# deprecated in favor of vmware_datastores and will be removed in the -# Liberty release. (string value) -#vmware_datastore_name = - -# Number of times VMware ESX/VC server API must be retried upon -# connection related issues. (integer value) -#vmware_api_retry_count = 10 - -# The interval used for polling remote tasks invoked on VMware ESX/VC -# server. (integer value) -#vmware_task_poll_interval = 5 - -# The name of the directory where the glance images will be stored in -# the VMware datastore. (string value) -#vmware_store_image_dir = /openstack_glance - -# Allow to perform insecure SSL requests to ESX/VC. (boolean value) -#vmware_api_insecure = false - -# A list of datastores where the image can be stored. This option may -# be specified multiple times for specifying multiple datastores. -# Either one of vmware_datastore_name or vmware_datastores is -# required. The datastore name should be specified after its -# datacenter path, seperated by ":". An optional weight may be given -# after the datastore name, seperated again by ":". Thus, the required -# format becomes ::. -# When adding an image, the datastore with highest weight will be -# selected, unless there is not enough free space available in cases -# where the image size is already known. If no weight is given, it is -# assumed to be zero and the directory will be considered for -# selection last. If multiple datastores have the same weight, then -# the one with the most free space available is selected. (multi -# valued) -#vmware_datastores = - -# Info to match when looking for cinder in the service catalog. Format -# is : separated values of the form: -# :: (string value) -#cinder_catalog_info = volume:cinder:publicURL - -# Override service catalog lookup with template for cinder endpoint -# e.g. http://localhost:8776/v1/%(project_id)s (string value) -#cinder_endpoint_template = - -# Region name of this node (string value) -#os_region_name = - -# Location of ca certicates file to use for cinder client requests. -# (string value) -#cinder_ca_certificates_file = - -# Number of cinderclient retries on failed http calls (integer value) -#cinder_http_retries = 3 - -# Allow to perform insecure SSL requests to cinder (boolean value) -#cinder_api_insecure = false - -# Version of the authentication service to use. Valid versions are 2 -# for keystone and 1 for swauth and rackspace. (deprecated) (string -# value) -#swift_store_auth_version = 2 - -# If True, swiftclient won't check for a valid SSL certificate when -# authenticating. (boolean value) -#swift_store_auth_insecure = false - -# A string giving the CA certificate file to use in SSL connections -# for verifying certs. (string value) -#swift_store_cacert = - -# The region of the swift endpoint to be used for single tenant. This -# setting is only necessary if the tenant has multiple swift -# endpoints. (string value) -#swift_store_region = - -# If set, the configured endpoint will be used. If None, the storage -# url from the auth response will be used. (string value) -#swift_store_endpoint = - -# A string giving the endpoint type of the swift service to use -# (publicURL, adminURL or internalURL). This setting is only used if -# swift_store_auth_version is 2. (string value) -#swift_store_endpoint_type = publicURL - -# A string giving the service type of the swift service to use. This -# setting is only used if swift_store_auth_version is 2. (string -# value) -#swift_store_service_type = object-store - -# Container within the account that the account should use for storing -# images in Swift when using single container mode. In multiple -# container mode, this will be the prefix for all containers. (string -# value) -#swift_store_container = glance - -# The size, in MB, that Glance will start chunking image files and do -# a large object manifest in Swift. (integer value) -#swift_store_large_object_size = 5120 - -# The amount of data written to a temporary disk buffer during the -# process of chunking the image file. (integer value) -#swift_store_large_object_chunk_size = 200 - -# A boolean value that determines if we create the container if it -# does not exist. (boolean value) -#swift_store_create_container_on_put = false - -# If set to True, enables multi-tenant storage mode which causes -# Glance images to be stored in tenant specific Swift accounts. -# (boolean value) -#swift_store_multi_tenant = false - -# When set to 0, a single-tenant store will only use one container to -# store all images. When set to an integer value between 1 and 32, a -# single-tenant store will use multiple containers to store images, -# and this value will determine how many containers are created.Used -# only when swift_store_multi_tenant is disabled. The total number of -# containers that will be used is equal to 16^N, so if this config -# option is set to 2, then 16^2=256 containers will be used to store -# images. (integer value) -#swift_store_multiple_containers_seed = 0 - -# A list of tenants that will be granted read/write access on all -# Swift containers created by Glance in multi-tenant mode. (list -# value) -#swift_store_admin_tenants = - -# If set to False, disables SSL layer compression of https swift -# requests. Setting to False may improve performance for images which -# are already in a compressed format, eg qcow2. (boolean value) -#swift_store_ssl_compression = true - -# The number of times a Swift download will be retried before the -# request fails. (integer value) -#swift_store_retry_get_count = 0 - -# The reference to the default swift account/backing store parameters -# to use for adding new images. (string value) -#default_swift_reference = ref1 - -# The address where the Swift authentication service is -# listening.(deprecated) (string value) -#swift_store_auth_address = - -# The user to authenticate against the Swift authentication service -# (deprecated) (string value) -#swift_store_user = - -# Auth key for the user authenticating against the Swift -# authentication service. (deprecated) (string value) -#swift_store_key = - -# The config file that has the swift account(s)configs. (string value) -#swift_store_config_file = - -# Hostname or IP address of the instance to connect to, or a mongodb -# URI, or a list of hostnames / mongodb URIs. If host is an IPv6 -# literal it must be enclosed in '[' and ']' characters following the -# RFC2732 URL syntax (e.g. '[::1]' for localhost) (string value) -#mongodb_store_uri = - -# Database to use (string value) -#mongodb_store_db = - - [matchmaker_redis] # diff --git a/etc/faafo.conf.sample b/etc/faafo.conf.sample index f8997a2..7d9a5d9 100644 --- a/etc/faafo.conf.sample +++ b/etc/faafo.conf.sample @@ -173,310 +173,6 @@ #control_exchange = openstack -[glance_store] - -# -# From glance.store -# - -# List of stores enabled (list value) -#stores = file,http - -# Default scheme to use to store image data. The scheme must be -# registered by one of the stores defined by the 'stores' config -# option. (string value) -#default_store = file - -# Minimum interval seconds to execute updating dynamic storage -# capabilities based on backend status then. It's not a periodic -# routine, the update logic will be executed only when interval -# seconds elapsed and an operation of store has triggered. The feature -# will be enabled only when the option value greater then zero. -# (integer value) -#store_capabilities_update_min_interval = 0 - -# -# From glance.store -# - -# Directory to which the Filesystem backend store writes images. -# (string value) -# Deprecated group/name - [DEFAULT]/filesystem_store_datadir -#filesystem_store_datadir = - -# List of directories and its priorities to which the Filesystem -# backend store writes images. (multi valued) -# Deprecated group/name - [DEFAULT]/filesystem_store_datadirs -#filesystem_store_datadirs = - -# The path to a file which contains the metadata to be returned with -# any location associated with this store. The file must contain a -# valid JSON object. The object should contain the keys 'id' and -# 'mountpoint'. The value for both keys should be 'string'. (string -# value) -# Deprecated group/name - [DEFAULT]/filesystem_store_metadata_file -#filesystem_store_metadata_file = - -# The required permission for created image file. In this way the user -# other service used, e.g. Nova, who consumes the image could be the -# exclusive member of the group that owns the files created. Assigning -# it less then or equal to zero means don't change the default -# permission of the file. This value will be decoded as an octal -# digit. (integer value) -# Deprecated group/name - [DEFAULT]/filesystem_store_file_perm -#filesystem_store_file_perm = 0 - -# RADOS images will be chunked into objects of this size (in -# megabytes). For best performance, this should be a power of two. -# (integer value) -#rbd_store_chunk_size = 8 - -# RADOS pool in which images are stored. (string value) -#rbd_store_pool = images - -# RADOS user to authenticate as (only applicable if using Cephx. If -# , a default will be chosen based on the client. section in -# rbd_store_ceph_conf) (string value) -#rbd_store_user = - -# Ceph configuration file path. If , librados will locate the -# default config. If using cephx authentication, this file should -# include a reference to the right keyring in a client. section -# (string value) -#rbd_store_ceph_conf = /etc/ceph/ceph.conf - -# The host where the S3 server is listening. (string value) -#s3_store_host = - -# The S3 query token access key. (string value) -#s3_store_access_key = - -# The S3 query token secret key. (string value) -#s3_store_secret_key = - -# The S3 bucket to be used to store the Glance data. (string value) -#s3_store_bucket = - -# The local directory where uploads will be staged before they are -# transferred into S3. (string value) -#s3_store_object_buffer_dir = - -# A boolean to determine if the S3 bucket should be created on upload -# if it does not exist or if an error should be returned to the user. -# (boolean value) -#s3_store_create_bucket_on_put = false - -# The S3 calling format used to determine the bucket. Either subdomain -# or path can be used. (string value) -#s3_store_bucket_url_format = subdomain - -# What size, in MB, should S3 start chunking image files and do a -# multipart upload in S3. (integer value) -#s3_store_large_object_size = 100 - -# What multipart upload part size, in MB, should S3 use when uploading -# parts. The size must be greater than or equal to 5M. (integer value) -#s3_store_large_object_chunk_size = 10 - -# The number of thread pools to perform a multipart upload in S3. -# (integer value) -#s3_store_thread_pools = 10 - -# Images will be chunked into objects of this size (in megabytes). For -# best performance, this should be a power of two. (integer value) -#sheepdog_store_chunk_size = 64 - -# Port of sheep daemon. (integer value) -#sheepdog_store_port = 7000 - -# IP address of sheep daemon. (string value) -#sheepdog_store_address = localhost - -# ESX/ESXi or vCenter Server target system. The server value can be an -# IP address or a DNS name. (string value) -#vmware_server_host = - -# Username for authenticating with VMware ESX/VC server. (string -# value) -#vmware_server_username = - -# Password for authenticating with VMware ESX/VC server. (string -# value) -#vmware_server_password = - -# DEPRECATED. Inventory path to a datacenter. If the -# vmware_server_host specified is an ESX/ESXi, the -# vmware_datacenter_path is optional. If specified, it should be "ha- -# datacenter". This option is deprecated in favor of vmware_datastores -# and will be removed in the Liberty release. (string value) -#vmware_datacenter_path = ha-datacenter - -# DEPRECATED. Datastore associated with the datacenter. This option is -# deprecated in favor of vmware_datastores and will be removed in the -# Liberty release. (string value) -#vmware_datastore_name = - -# Number of times VMware ESX/VC server API must be retried upon -# connection related issues. (integer value) -#vmware_api_retry_count = 10 - -# The interval used for polling remote tasks invoked on VMware ESX/VC -# server. (integer value) -#vmware_task_poll_interval = 5 - -# The name of the directory where the glance images will be stored in -# the VMware datastore. (string value) -#vmware_store_image_dir = /openstack_glance - -# Allow to perform insecure SSL requests to ESX/VC. (boolean value) -#vmware_api_insecure = false - -# A list of datastores where the image can be stored. This option may -# be specified multiple times for specifying multiple datastores. -# Either one of vmware_datastore_name or vmware_datastores is -# required. The datastore name should be specified after its -# datacenter path, seperated by ":". An optional weight may be given -# after the datastore name, seperated again by ":". Thus, the required -# format becomes ::. -# When adding an image, the datastore with highest weight will be -# selected, unless there is not enough free space available in cases -# where the image size is already known. If no weight is given, it is -# assumed to be zero and the directory will be considered for -# selection last. If multiple datastores have the same weight, then -# the one with the most free space available is selected. (multi -# valued) -#vmware_datastores = - -# Info to match when looking for cinder in the service catalog. Format -# is : separated values of the form: -# :: (string value) -#cinder_catalog_info = volume:cinder:publicURL - -# Override service catalog lookup with template for cinder endpoint -# e.g. http://localhost:8776/v1/%(project_id)s (string value) -#cinder_endpoint_template = - -# Region name of this node (string value) -#os_region_name = - -# Location of ca certicates file to use for cinder client requests. -# (string value) -#cinder_ca_certificates_file = - -# Number of cinderclient retries on failed http calls (integer value) -#cinder_http_retries = 3 - -# Allow to perform insecure SSL requests to cinder (boolean value) -#cinder_api_insecure = false - -# Version of the authentication service to use. Valid versions are 2 -# for keystone and 1 for swauth and rackspace. (deprecated) (string -# value) -#swift_store_auth_version = 2 - -# If True, swiftclient won't check for a valid SSL certificate when -# authenticating. (boolean value) -#swift_store_auth_insecure = false - -# A string giving the CA certificate file to use in SSL connections -# for verifying certs. (string value) -#swift_store_cacert = - -# The region of the swift endpoint to be used for single tenant. This -# setting is only necessary if the tenant has multiple swift -# endpoints. (string value) -#swift_store_region = - -# If set, the configured endpoint will be used. If None, the storage -# url from the auth response will be used. (string value) -#swift_store_endpoint = - -# A string giving the endpoint type of the swift service to use -# (publicURL, adminURL or internalURL). This setting is only used if -# swift_store_auth_version is 2. (string value) -#swift_store_endpoint_type = publicURL - -# A string giving the service type of the swift service to use. This -# setting is only used if swift_store_auth_version is 2. (string -# value) -#swift_store_service_type = object-store - -# Container within the account that the account should use for storing -# images in Swift when using single container mode. In multiple -# container mode, this will be the prefix for all containers. (string -# value) -#swift_store_container = glance - -# The size, in MB, that Glance will start chunking image files and do -# a large object manifest in Swift. (integer value) -#swift_store_large_object_size = 5120 - -# The amount of data written to a temporary disk buffer during the -# process of chunking the image file. (integer value) -#swift_store_large_object_chunk_size = 200 - -# A boolean value that determines if we create the container if it -# does not exist. (boolean value) -#swift_store_create_container_on_put = false - -# If set to True, enables multi-tenant storage mode which causes -# Glance images to be stored in tenant specific Swift accounts. -# (boolean value) -#swift_store_multi_tenant = false - -# When set to 0, a single-tenant store will only use one container to -# store all images. When set to an integer value between 1 and 32, a -# single-tenant store will use multiple containers to store images, -# and this value will determine how many containers are created.Used -# only when swift_store_multi_tenant is disabled. The total number of -# containers that will be used is equal to 16^N, so if this config -# option is set to 2, then 16^2=256 containers will be used to store -# images. (integer value) -#swift_store_multiple_containers_seed = 0 - -# A list of tenants that will be granted read/write access on all -# Swift containers created by Glance in multi-tenant mode. (list -# value) -#swift_store_admin_tenants = - -# If set to False, disables SSL layer compression of https swift -# requests. Setting to False may improve performance for images which -# are already in a compressed format, eg qcow2. (boolean value) -#swift_store_ssl_compression = true - -# The number of times a Swift download will be retried before the -# request fails. (integer value) -#swift_store_retry_get_count = 0 - -# The reference to the default swift account/backing store parameters -# to use for adding new images. (string value) -#default_swift_reference = ref1 - -# The address where the Swift authentication service is -# listening.(deprecated) (string value) -#swift_store_auth_address = - -# The user to authenticate against the Swift authentication service -# (deprecated) (string value) -#swift_store_user = - -# Auth key for the user authenticating against the Swift -# authentication service. (deprecated) (string value) -#swift_store_key = - -# The config file that has the swift account(s)configs. (string value) -#swift_store_config_file = - -# Hostname or IP address of the instance to connect to, or a mongodb -# URI, or a list of hostnames / mongodb URIs. If host is an IPv6 -# literal it must be enclosed in '[' and ']' characters following the -# RFC2732 URL syntax (e.g. '[::1]' for localhost) (string value) -#mongodb_store_uri = - -# Database to use (string value) -#mongodb_store_db = - - [matchmaker_redis] # diff --git a/etc/oslo-config-generator/faafo.conf b/etc/oslo-config-generator/faafo.conf index 74063ae..d96d12a 100644 --- a/etc/oslo-config-generator/faafo.conf +++ b/etc/oslo-config-generator/faafo.conf @@ -4,4 +4,3 @@ namespace = faafo.worker namespace = faafo.api namespace = oslo.messaging namespace = oslo.log -namespace = glance.store diff --git a/faafo/api/service.py b/faafo/api/service.py index 033e831..fc84dae 100644 --- a/faafo/api/service.py +++ b/faafo/api/service.py @@ -10,23 +10,24 @@ # License for the specific language governing permissions and limitations # under the License. +import base64 import copy +import cStringIO from pkg_resources import resource_filename import flask import flask.ext.restless import flask.ext.sqlalchemy from flask_bootstrap import Bootstrap -import glance_store from oslo_config import cfg from oslo_log import log import oslo_messaging as messaging +from PIL import Image from faafo import version LOG = log.getLogger('faafo.api') CONF = cfg.CONF -glance_store.register_opts(CONF) api_opts = [ cfg.StrOpt('listen-address', @@ -59,9 +60,6 @@ app.config['SQLALCHEMY_DATABASE_URI'] = CONF.database_url db = flask.ext.sqlalchemy.SQLAlchemy(app) Bootstrap(app) -glance_store.create_stores(CONF) -glance_store.verify_default_store() - def list_opts(): """Entry point for oslo-config-generator.""" @@ -81,6 +79,7 @@ class Fractal(db.Model): xb = db.Column(db.Float, nullable=False) ya = db.Column(db.Float, nullable=False) yb = db.Column(db.Float, nullable=False) + image = db.Column(db.LargeBinary, nullable=True) def __repr__(self): return '' % self.uuid @@ -112,8 +111,12 @@ def get_fractal(fractalid): 'message': 'Fracal not found'}) response.status_code = 404 else: - image, imagesize = glance_store.get_from_backend(fractal.url) - response = flask.make_response(image.fp.read()) + image_data = base64.b64decode(fractal.image) + image = Image.open(cStringIO.StringIO(image_data)) + output = cStringIO.StringIO() + image.save(output, "PNG") + image.seek(0) + response = flask.make_response(output.getvalue()) response.content_type = "image/png" return response diff --git a/faafo/worker/service.py b/faafo/worker/service.py index c9caa57..aeb199f 100644 --- a/faafo/worker/service.py +++ b/faafo/worker/service.py @@ -17,6 +17,7 @@ import eventlet eventlet.monkey_patch() +import base64 import copy import hashlib import json @@ -27,7 +28,6 @@ import socket import tempfile import time -import glance_store from oslo_config import cfg from oslo_log import log import oslo_messaging as messaging @@ -36,7 +36,6 @@ import requests LOG = log.getLogger('faafo.worker') CONF = cfg.CONF -glance_store.register_opts(CONF) worker_opts = { @@ -123,19 +122,19 @@ class WorkerEndpoint(object): filename = juliaset.get_file() LOG.debug("saved result of task %s to temporary file %s" % (task['uuid'], filename)) - with open(filename, 'rb') as fp: + with open(filename, "rb") as fp: size = os.fstat(fp.fileno()).st_size - glance = glance_store.add_to_backend(CONF, task['uuid'], fp, size) + image = base64.b64encode(fp.read()) checksum = hashlib.sha256(open(filename, 'rb').read()).hexdigest() - LOG.debug("checksum for task %s: %s" % (task['uuid'], checksum)) os.remove(filename) + LOG.debug("removed temporary file %s" % task['uuid'], filename) result = { 'uuid': task['uuid'], 'duration': elapsed_time, + 'image': image, 'checksum': checksum, - 'url': glance[0], - 'size': glance[1] + 'size': size } # NOTE(berendt): only necessary when using requests < 2.4.2 @@ -158,7 +157,4 @@ def get_server(): server = messaging.get_rpc_server(transport, target, endpoints, executor='eventlet') - glance_store.create_stores(CONF) - glance_store.verify_default_store() - return server diff --git a/requirements.txt b/requirements.txt index 3379fd1..82eabca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,9 +11,4 @@ flask-sqlalchemy oslo.config>=1.9.3,<1.10.0 # Apache-2.0 oslo.log>=1.0.0,<1.1.0 # Apache-2.0 oslo.messaging>=1.8.0,<1.9.0 # Apache-2.0 -glance_store>=0.3.0 # Apache-2.0 PrettyTable>=0.7,<0.8 - -# dependencies of the glance.store library -python-swiftclient>=2.2.0 -oslo.vmware>=0.11.0 # Apache-2.0