| # |
| # |
| |
| # Copyright (C) 2006, 2007, 2008 Google Inc. |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, but |
| # WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| # General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write to the Free Software |
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| # 02110-1301, USA. |
| |
| """Remote API connection map. |
| |
| """ |
| |
| # pylint: disable=C0103 |
| |
| # C0103: Invalid name, since the R_* names are not conforming |
| |
| import re |
| import urlparse |
| |
| from ganeti import constants |
| from ganeti import http |
| from ganeti import utils |
| |
| from ganeti.rapi import rlib2 |
| |
| |
| _NAME_PATTERN = r"[\w\._-]+" |
| _DISK_PATTERN = r"\d+" |
| |
| # the connection map is created at the end of this file |
| CONNECTOR = {} |
| |
| |
| class Mapper: |
| """Map resource to method. |
| |
| """ |
| def __init__(self, connector=None): |
| """Resource mapper constructor. |
| |
| @param connector: a dictionary, mapping method name with URL path regexp |
| |
| """ |
| if connector is None: |
| connector = CONNECTOR |
| self._connector = connector |
| |
| def getController(self, uri): |
| """Find method for a given URI. |
| |
| @param uri: string with URI |
| |
| @return: None if no method is found or a tuple containing |
| the following fields: |
| - method: name of method mapped to URI |
| - items: a list of variable intems in the path |
| - args: a dictionary with additional parameters from URL |
| |
| """ |
| if "?" in uri: |
| (path, query) = uri.split("?", 1) |
| args = urlparse.parse_qs(query) |
| else: |
| path = uri |
| query = None |
| args = {} |
| |
| # Try to find handler for request path |
| result = utils.FindMatch(self._connector, path) |
| |
| if result is None: |
| raise http.HttpNotFound() |
| |
| (handler, groups) = result |
| |
| return (handler, groups, args) |
| |
| |
| def _ConvertPattern(value): |
| """Converts URI pattern into a regular expression group. |
| |
| Used by L{_CompileHandlerPath}. |
| |
| """ |
| if isinstance(value, UriPattern): |
| return "(%s)" % value.content |
| else: |
| return value |
| |
| |
| def _CompileHandlerPath(*args): |
| """Compiles path for RAPI resource into regular expression. |
| |
| @return: Compiled regular expression object |
| |
| """ |
| return re.compile("^%s$" % "".join(map(_ConvertPattern, args))) |
| |
| |
| class UriPattern(object): |
| __slots__ = [ |
| "content", |
| ] |
| |
| def __init__(self, content): |
| self.content = content |
| |
| |
| def GetHandlers(node_name_pattern, instance_name_pattern, |
| group_name_pattern, network_name_pattern, |
| job_id_pattern, disk_pattern, |
| query_res_pattern, |
| translate=None): |
| """Returns all supported resources and their handlers. |
| |
| C{node_name_pattern} and the other C{*_pattern} parameters are wrapped in |
| L{UriPattern} and, if used in a URI, passed to the function specified using |
| C{translate}. C{translate} receives 1..N parameters which are either plain |
| strings or instances of L{UriPattern} and returns a dictionary key suitable |
| for the caller of C{GetHandlers}. The default implementation in |
| L{_CompileHandlerPath} returns a compiled regular expression in which each |
| pattern is a group. |
| |
| @rtype: dict |
| |
| """ |
| if translate is None: |
| translate_fn = _CompileHandlerPath |
| else: |
| translate_fn = translate |
| |
| node_name = UriPattern(node_name_pattern) |
| instance_name = UriPattern(instance_name_pattern) |
| group_name = UriPattern(group_name_pattern) |
| network_name = UriPattern(network_name_pattern) |
| job_id = UriPattern(job_id_pattern) |
| disk = UriPattern(disk_pattern) |
| query_res = UriPattern(query_res_pattern) |
| |
| # Important note: New resources should always be added under /2. During a |
| # discussion in July 2010 it was decided that having per-resource versions |
| # is more flexible and future-compatible than versioning the whole remote |
| # API. |
| # TODO: Consider a different data structure where all keys are of the same |
| # type. Strings are faster to look up in a dictionary than iterating and |
| # matching regular expressions, therefore maybe two separate dictionaries |
| # should be used. |
| return { |
| "/": rlib2.R_root, |
| "/2": rlib2.R_2, |
| |
| "/version": rlib2.R_version, |
| |
| "/2/nodes": rlib2.R_2_nodes, |
| |
| translate_fn("/2/nodes/", node_name): |
| rlib2.R_2_nodes_name, |
| translate_fn("/2/nodes/", node_name, "/powercycle"): |
| rlib2.R_2_nodes_name_powercycle, |
| translate_fn("/2/nodes/", node_name, "/tags"): |
| rlib2.R_2_nodes_name_tags, |
| translate_fn("/2/nodes/", node_name, "/role"): |
| rlib2.R_2_nodes_name_role, |
| translate_fn("/2/nodes/", node_name, "/evacuate"): |
| rlib2.R_2_nodes_name_evacuate, |
| translate_fn("/2/nodes/", node_name, "/migrate"): |
| rlib2.R_2_nodes_name_migrate, |
| translate_fn("/2/nodes/", node_name, "/modify"): |
| rlib2.R_2_nodes_name_modify, |
| translate_fn("/2/nodes/", node_name, "/storage"): |
| rlib2.R_2_nodes_name_storage, |
| translate_fn("/2/nodes/", node_name, "/storage/modify"): |
| rlib2.R_2_nodes_name_storage_modify, |
| translate_fn("/2/nodes/", node_name, "/storage/repair"): |
| rlib2.R_2_nodes_name_storage_repair, |
| |
| "/2/instances": rlib2.R_2_instances, |
| translate_fn("/2/instances/", instance_name): |
| rlib2.R_2_instances_name, |
| translate_fn("/2/instances/", instance_name, "/info"): |
| rlib2.R_2_instances_name_info, |
| translate_fn("/2/instances/", instance_name, "/tags"): |
| rlib2.R_2_instances_name_tags, |
| translate_fn("/2/instances/", instance_name, "/reboot"): |
| rlib2.R_2_instances_name_reboot, |
| translate_fn("/2/instances/", instance_name, "/reinstall"): |
| rlib2.R_2_instances_name_reinstall, |
| translate_fn("/2/instances/", instance_name, "/replace-disks"): |
| rlib2.R_2_instances_name_replace_disks, |
| translate_fn("/2/instances/", instance_name, "/shutdown"): |
| rlib2.R_2_instances_name_shutdown, |
| translate_fn("/2/instances/", instance_name, "/startup"): |
| rlib2.R_2_instances_name_startup, |
| translate_fn("/2/instances/", instance_name, "/activate-disks"): |
| rlib2.R_2_instances_name_activate_disks, |
| translate_fn("/2/instances/", instance_name, "/deactivate-disks"): |
| rlib2.R_2_instances_name_deactivate_disks, |
| translate_fn("/2/instances/", instance_name, "/recreate-disks"): |
| rlib2.R_2_instances_name_recreate_disks, |
| translate_fn("/2/instances/", instance_name, "/prepare-export"): |
| rlib2.R_2_instances_name_prepare_export, |
| translate_fn("/2/instances/", instance_name, "/export"): |
| rlib2.R_2_instances_name_export, |
| translate_fn("/2/instances/", instance_name, "/migrate"): |
| rlib2.R_2_instances_name_migrate, |
| translate_fn("/2/instances/", instance_name, "/failover"): |
| rlib2.R_2_instances_name_failover, |
| translate_fn("/2/instances/", instance_name, "/rename"): |
| rlib2.R_2_instances_name_rename, |
| translate_fn("/2/instances/", instance_name, "/modify"): |
| rlib2.R_2_instances_name_modify, |
| translate_fn("/2/instances/", instance_name, "/disk/", disk, "/grow"): |
| rlib2.R_2_instances_name_disk_grow, |
| translate_fn("/2/instances/", instance_name, "/console"): |
| rlib2.R_2_instances_name_console, |
| |
| "/2/networks": rlib2.R_2_networks, |
| translate_fn("/2/networks/", network_name): |
| rlib2.R_2_networks_name, |
| translate_fn("/2/networks/", network_name, "/connect"): |
| rlib2.R_2_networks_name_connect, |
| translate_fn("/2/networks/", network_name, "/disconnect"): |
| rlib2.R_2_networks_name_disconnect, |
| translate_fn("/2/networks/", network_name, "/modify"): |
| rlib2.R_2_networks_name_modify, |
| translate_fn("/2/networks/", network_name, "/tags"): |
| rlib2.R_2_networks_name_tags, |
| |
| "/2/groups": rlib2.R_2_groups, |
| translate_fn("/2/groups/", group_name): |
| rlib2.R_2_groups_name, |
| translate_fn("/2/groups/", group_name, "/modify"): |
| rlib2.R_2_groups_name_modify, |
| translate_fn("/2/groups/", group_name, "/rename"): |
| rlib2.R_2_groups_name_rename, |
| translate_fn("/2/groups/", group_name, "/assign-nodes"): |
| rlib2.R_2_groups_name_assign_nodes, |
| translate_fn("/2/groups/", group_name, "/tags"): |
| rlib2.R_2_groups_name_tags, |
| |
| "/2/jobs": rlib2.R_2_jobs, |
| translate_fn("/2/jobs/", job_id): |
| rlib2.R_2_jobs_id, |
| translate_fn("/2/jobs/", job_id, "/wait"): |
| rlib2.R_2_jobs_id_wait, |
| |
| "/2/instances-multi-alloc": rlib2.R_2_instances_multi_alloc, |
| "/2/tags": rlib2.R_2_tags, |
| "/2/info": rlib2.R_2_info, |
| "/2/os": rlib2.R_2_os, |
| "/2/redistribute-config": rlib2.R_2_redist_config, |
| "/2/features": rlib2.R_2_features, |
| "/2/modify": rlib2.R_2_cluster_modify, |
| |
| translate_fn("/2/query/", query_res): |
| rlib2.R_2_query, |
| translate_fn("/2/query/", query_res, "/fields"): |
| rlib2.R_2_query_fields, |
| } |
| |
| |
| CONNECTOR.update(GetHandlers(_NAME_PATTERN, _NAME_PATTERN, |
| _NAME_PATTERN, _NAME_PATTERN, |
| constants.JOB_ID_TEMPLATE, _DISK_PATTERN, |
| _NAME_PATTERN)) |