blob: c972c4d6de7620f78bd05796dfb0ba35e4492b62 [file] [log] [blame]
#!/usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
from __future__ import with_statement
import os
import sys
import inspect
from collections import OrderedDict
from os.path import join as pjoin
this_dir = os.path.abspath(os.path.split(__file__)[0])
sys.path.insert(0, os.path.join(this_dir, "../"))
from libcloud.compute.base import NodeDriver
from libcloud.compute.providers import get_driver as get_compute_driver
from libcloud.compute.providers import DRIVERS as COMPUTE_DRIVERS
from libcloud.compute.types import Provider as ComputeProvider
from libcloud.loadbalancer.base import Driver as LBDriver
from libcloud.loadbalancer.providers import get_driver as get_lb_driver
from libcloud.loadbalancer.providers import DRIVERS as LB_DRIVERS
from libcloud.loadbalancer.types import Provider as LBProvider
from libcloud.storage.base import StorageDriver
from libcloud.storage.providers import get_driver as get_storage_driver
from libcloud.storage.providers import DRIVERS as STORAGE_DRIVERS
from libcloud.storage.types import Provider as StorageProvider
from libcloud.dns.base import DNSDriver
from libcloud.dns.providers import get_driver as get_dns_driver
from libcloud.dns.providers import DRIVERS as DNS_DRIVERS
from libcloud.dns.types import Provider as DNSProvider
from libcloud.container.base import ContainerDriver
from libcloud.container.providers import get_driver as get_container_driver
from libcloud.container.providers import DRIVERS as CONTAINER_DRIVERS
from libcloud.container.types import Provider as ContainerProvider
from libcloud.backup.base import BackupDriver
from libcloud.backup.providers import get_driver as get_backup_driver
from libcloud.backup.providers import DRIVERS as BACKUP_DRIVERS
from libcloud.backup.types import Provider as BackupProvider
HEADER = (
".. NOTE: This file has been generated automatically using "
"generate_provider_feature_matrix_table.py script, don't manually "
"edit it"
)
BASE_API_METHODS = {
"compute_main": [
"list_nodes",
"create_node",
"reboot_node",
"destroy_node",
"start_node",
"stop_node",
"list_images",
"list_sizes",
"deploy_node",
],
"compute_image_management": [
"list_images",
"get_image",
"create_image",
"delete_image",
"copy_image",
],
"compute_block_storage": [
"list_volumes",
"create_volume",
"destroy_volume",
"attach_volume",
"detach_volume",
"list_volume_snapshots",
"create_volume_snapshot",
],
"compute_key_pair_management": [
"list_key_pairs",
"get_key_pair",
"create_key_pair",
"import_key_pair_from_string",
"import_key_pair_from_file",
"delete_key_pair",
],
"loadbalancer": [
"create_balancer",
"list_balancers",
"balancer_list_members",
"balancer_attach_member",
"balancer_detach_member",
"balancer_attach_compute_node",
],
"storage_main": [
"list_containers",
"list_container_objects",
"iterate_containers",
"iterate_container_objects",
"create_container",
"delete_container",
"upload_object",
"upload_object_via_stream",
"download_object",
"download_object_range",
"download_object_as_stream",
"download_object_range_as_stream",
"delete_object",
],
"storage_cdn": [
"enable_container_cdn",
"enable_object_cdn",
"get_container_cdn_url",
"get_object_cdn_url",
],
"dns": [
"list_zones",
"list_records",
"iterate_zones",
"iterate_records",
"create_zone",
"update_zone",
"create_record",
"update_record",
"delete_zone",
"delete_record",
],
"container": [
"install_image",
"list_images",
"deploy_container",
"get_container",
"start_container",
"stop_container",
"restart_container",
"destroy_container",
"list_containers",
"list_locations",
"create_cluster",
"destroy_cluster",
"list_clusters",
],
"backup": [
"get_supported_target_types",
"list_targets",
"create_target",
"create_target_from_node",
"create_target_from_storage_container",
"update_target",
"delete_target",
"list_recovery_points",
"recover_target",
"recover_target_out_of_place",
"list_target_jobs",
"create_target_job",
"resume_target_job",
"suspend_target_job",
"cancel_target_job",
],
}
FRIENDLY_METHODS_NAMES = {
"compute_main": {
"list_nodes": "list nodes",
"create_node": "create node",
"reboot_node": "reboot node",
"start_node": "start node",
"stop_node": "stop node",
"destroy_node": "destroy node",
"list_images": "list images",
"list_sizes": "list sizes",
"deploy_node": "deploy node",
},
"compute_image_management": {
"list_images": "list images",
"get_image": "get image",
"create_image": "create image",
"copy_image": "copy image",
"delete_image": "delete image",
},
"compute_block_storage": {
"list_volumes": "list volumes",
"create_volume": "create volume",
"destroy_volume": "destroy volume",
"attach_volume": "attach volume",
"detach_volume": "detach volume",
"list_volume_snapshots": "list snapshots",
"create_volume_snapshot": "create snapshot",
},
"compute_key_pair_management": {
"list_key_pairs": "list key pairs",
"get_key_pair": "get key pair",
"create_key_pair": "create key pair",
"import_key_pair_from_string": "import public key from string",
"import_key_pair_from_file": "import public key from file",
"delete_key_pair": "delete key pair",
},
"loadbalancer": {
"create_balancer": "create balancer",
"list_balancers": "list balancers",
"balancer_list_members": "list members",
"balancer_attach_member": "attach member",
"balancer_detach_member": "detach member",
"balancer_attach_compute_node": "attach compute node",
},
"storage_main": {
"list_containers": "list containers",
"list_container_objects": "list objects",
"create_container": "create container",
"delete_container": "delete container",
"upload_object": "upload object",
"upload_object_via_stream": "streaming object upload",
"download_object": "download object",
"download_object_as_stream": "streaming object download",
"download_object_range": "download part of an object",
"download_object_range_as_stream": "streaming partial object download",
"delete_object": "delete object",
},
"storage_cdn": {
"enable_container_cdn": "enable container cdn",
"enable_object_cdn": "enable object cdn",
"get_container_cdn_url": "get container cdn URL",
"get_object_cdn_url": "get object cdn URL",
},
"dns": {
"list_zones": "list zones",
"list_records": "list records",
"create_zone": "create zone",
"update_zone": "update zone",
"create_record": "create record",
"update_record": "update record",
"delete_zone": "delete zone",
"delete_record": "delete record",
},
"container": {
"install_image": "install image",
"list_images": "list images",
"deploy_container": "deploy container",
"get_container": "get container",
"list_containers": "list containers",
"start_container": "start container",
"stop_container": "stop container",
"restart_container": "restart container",
"destroy_container": "destroy container",
"list_locations": "list locations",
"create_cluster": "create cluster",
"destroy_cluster": "destroy cluster",
"list_clusters": "list clusters",
},
"backup": {
"get_supported_target_types": "get supported target types",
"list_targets": "list targets",
"create_target": "create target",
"create_target_from_node": "create target from node",
"create_target_from_storage_container": "create target from storage container",
"update_target": "update target",
"delete_target": "delete target",
"list_recovery_points": "list recovery points",
"recover_target": "recover target",
"recover_target_out_of_place": "recover target out of place",
"list_target_jobs": "list target jobs",
"create_target_job": "create target job",
"resume_target_job": "resume target job",
"suspend_target_job": "suspend target job",
"cancel_target_job": "cancel target job",
},
}
IGNORED_PROVIDERS = [
"dummy",
# Deprecated constants
"cloudsigma_us",
"cloudfiles_swift",
]
def get_provider_api_names(Provider):
names = [
key for key, value in Provider.__dict__.items() if not key.startswith("__")
]
return names
def generate_providers_table(api):
result = {}
if api in [
"compute_main",
"compute_image_management",
"compute_block_storage",
"compute_key_pair_management",
]:
driver = NodeDriver
drivers = COMPUTE_DRIVERS
provider = ComputeProvider
get_driver_method = get_compute_driver
elif api == "loadbalancer":
driver = LBDriver
drivers = LB_DRIVERS
provider = LBProvider
get_driver_method = get_lb_driver
elif api in ["storage_main", "storage_cdn"]:
driver = StorageDriver
drivers = STORAGE_DRIVERS
provider = StorageProvider
get_driver_method = get_storage_driver
elif api == "dns":
driver = DNSDriver
drivers = DNS_DRIVERS
provider = DNSProvider
get_driver_method = get_dns_driver
elif api == "container":
driver = ContainerDriver
drivers = CONTAINER_DRIVERS
provider = ContainerProvider
get_driver_method = get_container_driver
elif api == "backup":
driver = BackupDriver
drivers = BACKUP_DRIVERS
provider = BackupProvider
get_driver_method = get_backup_driver
else:
raise Exception("Invalid api: %s" % (api))
names = get_provider_api_names(provider)
result = OrderedDict()
for name in names:
enum = getattr(provider, name)
try:
cls = get_driver_method(enum)
except Exception as e:
# Deprecated providers throw an exception
print('Ignoring deprecated constant "%s": %s' % (enum, str(e)))
continue
# Hack for providers which expose multiple classes and support multiple
# API versions
# TODO: Make entry per version
if name.lower() == "cloudsigma":
from libcloud.compute.drivers.cloudsigma import CloudSigma_2_0_NodeDriver
cls = CloudSigma_2_0_NodeDriver
elif name.lower() == "opennebula":
from libcloud.compute.drivers.opennebula import OpenNebula_3_8_NodeDriver
cls = OpenNebula_3_8_NodeDriver
elif name.lower() == "digital_ocean" and api.startswith("compute"):
from libcloud.compute.drivers.digitalocean import DigitalOcean_v2_NodeDriver
cls = DigitalOcean_v2_NodeDriver
elif name.lower() == "linode" and api.startswith("compute"):
from libcloud.compute.drivers.linode import LinodeNodeDriverV4
cls = LinodeNodeDriverV4
elif name.lower() == "linode" and api.startswith("dns"):
from libcloud.dns.drivers.linode import LinodeDNSDriverV4
cls = LinodeDNSDriverV4
elif name.lower() == "vultr" and api.startswith("compute"):
from libcloud.compute.drivers.vultr import VultrNodeDriverV2
cls = VultrNodeDriverV2
elif name.lower() == "vultr" and api.startswith("dns"):
from libcloud.dns.drivers.vultr import VultrDNSDriverV2
cls = VultrDNSDriverV2
if name.lower() in IGNORED_PROVIDERS:
continue
def is_function_or_method(*args, **kwargs):
return inspect.isfunction(*args, **kwargs) or inspect.ismethod(
*args, **kwargs
)
driver_methods = dict(inspect.getmembers(cls, predicate=is_function_or_method))
base_methods = dict(inspect.getmembers(driver, predicate=is_function_or_method))
base_api_methods = BASE_API_METHODS[api]
result[name] = {
"name": cls.name,
"website": cls.website,
"constant": name,
"module": drivers[enum][0],
"class": drivers[enum][1],
"cls": cls,
"methods": {},
}
print("Generating tables for provider: %s" % (name))
for method_name in base_api_methods:
base_method = base_methods[method_name]
if method_name == "deploy_node":
features = getattr(cls, "features", {}).get("create_node", [])
is_implemented = len(features) >= 1
else:
if method_name not in driver_methods:
is_implemented = False
else:
driver_method = driver_methods[method_name]
# NOTE: id() is not safe
# is_implemented = (id(driver_method) != id(base_method))
is_implemented = driver_method != base_method
result[name]["methods"][method_name] = is_implemented
return result
def generate_rst_table(data):
cols = len(data[0])
col_len = [max(len(r[i]) for r in data) for i in range(cols)]
formatter = " ".join("{:<%d}" % c for c in col_len)
header = formatter.format(*["=" * c for c in col_len])
rows = [formatter.format(*row) for row in data]
result = (
header
+ "\n"
+ rows[0]
+ "\n"
+ header
+ "\n"
+ "\n".join(rows[1:])
+ "\n"
+ header
)
return result
def generate_supported_methods_table(api, provider_matrix):
base_api_methods = BASE_API_METHODS[api]
data = []
header = [
FRIENDLY_METHODS_NAMES[api][method_name]
for method_name in base_api_methods
if not method_name.startswith("iterate_")
]
data.append(["Provider"] + header)
for provider, values in sorted(provider_matrix.items()):
provider_name = "`%s`_" % (values["name"])
row = [provider_name]
# TODO: Make it nicer
# list_* methods don't need to be implemented if iterate_* methods are
# implemented
if api == "storage_main":
if values["methods"]["iterate_containers"]:
values["methods"]["list_containers"] = True
if values["methods"]["iterate_container_objects"]:
values["methods"]["list_container_objects"] = True
elif api == "dns":
# list_zones and list_records don't need to be implemented if
if values["methods"]["iterate_zones"]:
values["methods"]["list_zones"] = True
if values["methods"]["iterate_records"]:
values["methods"]["list_records"] = True
for method in base_api_methods:
# TODO: ghetto
if method.startswith("iterate_"):
continue
supported = values["methods"][method]
if supported:
row.append("yes")
else:
row.append("no")
data.append(row)
result = generate_rst_table(data)
result += "\n\n"
for provider, values in sorted(provider_matrix.items()):
result += ".. _`%s`: %s\n" % (values["name"], values["website"])
return result
def generate_supported_providers_table(api, provider_matrix):
data = []
header = [
"Provider",
"Documentation",
"Provider Constant",
"Supported Regions",
"Module",
"Class Name",
]
data.append(header)
for provider, values in sorted(provider_matrix.items()):
name_str = "`%s`_" % (values["name"])
module_str = ":mod:`%s`" % (values["module"])
class_str = ":class:`%s`" % (values["class"])
# Ignore old deprecated driver class per region S3 drivers
if "Amazon S3 (" in values["name"]:
continue
params = {"api": api, "provider": provider.lower()}
driver_docs_path = pjoin(
this_dir, "../docs/%(api)s/drivers/%(provider)s.rst" % params
)
if os.path.exists(driver_docs_path):
docs_link = ":doc:`Click </%(api)s/drivers/%(provider)s>`" % params
else:
docs_link = ""
cls = values["cls"]
supported_regions = cls.list_regions() if hasattr(cls, "list_regions") else None
if supported_regions:
# Sort the regions to achieve stable output
supported_regions = sorted(supported_regions)
supported_regions = ", ".join(supported_regions)
else:
supported_regions = "single region driver"
row = [
name_str,
docs_link,
values["constant"],
supported_regions,
module_str,
class_str,
]
data.append(row)
result = generate_rst_table(data)
result += "\n\n"
for provider, values in sorted(provider_matrix.items()):
result += ".. _`%s`: %s\n" % (values["name"], values["website"])
return result
def generate_tables():
apis = BASE_API_METHODS.keys()
for api in apis:
result = generate_providers_table(api)
docs_dir = api
if api.startswith("compute"):
docs_dir = "compute"
elif api.startswith("storage"):
docs_dir = "storage"
supported_providers = generate_supported_providers_table(docs_dir, result)
supported_methods = generate_supported_methods_table(api, result)
current_path = os.path.dirname(__file__)
target_dir = os.path.abspath(pjoin(current_path, "../docs/%s/" % (docs_dir)))
file_name_1 = "_supported_providers.rst"
file_name_2 = "_supported_methods.rst"
if api == "compute_main":
file_name_2 = "_supported_methods_main.rst"
elif api == "compute_image_management":
file_name_2 = "_supported_methods_image_management.rst"
elif api == "compute_block_storage":
file_name_2 = "_supported_methods_block_storage.rst"
elif api == "compute_key_pair_management":
file_name_2 = "_supported_methods_key_pair_management.rst"
elif api == "storage_main":
file_name_2 = "_supported_methods_main.rst"
elif api == "storage_cdn":
file_name_2 = "_supported_methods_cdn.rst"
supported_providers_path = pjoin(target_dir, file_name_1)
supported_methods_path = pjoin(target_dir, file_name_2)
with open(supported_providers_path, "w") as fp:
fp.write(HEADER + "\n\n")
fp.write(supported_providers)
with open(supported_methods_path, "w") as fp:
fp.write(HEADER + "\n\n")
fp.write(supported_methods)
generate_tables()