4 # Copyright (C) 2006, 2007, 2008 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 """Remote API connection map.
25 # pylint: disable-msg=C0103
27 # C0103: Invalid name, since the R_* names are not conforming
32 from ganeti import constants
33 from ganeti import http
34 from ganeti import utils
36 from ganeti.rapi import baserlib
37 from ganeti.rapi import rlib2
40 _NAME_PATTERN = r"[\w\._-]+"
41 _DISK_PATTERN = r"\d+"
43 # the connection map is created at the end of this file
48 """Map resource to method.
51 def __init__(self, connector=None):
52 """Resource mapper constructor.
54 @param connector: a dictionary, mapping method name with URL path regexp
59 self._connector = connector
61 def getController(self, uri):
62 """Find method for a given URI.
64 @param uri: string with URI
66 @return: None if no method is found or a tuple containing
68 - method: name of method mapped to URI
69 - items: a list of variable intems in the path
70 - args: a dictionary with additional parameters from URL
74 (path, query) = uri.split('?', 1)
75 args = cgi.parse_qs(query)
81 # Try to find handler for request path
82 result = utils.FindMatch(self._connector, path)
85 raise http.HttpNotFound()
87 (handler, groups) = result
89 return (handler, groups, args)
92 class R_root(baserlib.R_Generic):
96 _ROOT_PATTERN = re.compile("^R_([a-zA-Z0-9]+)$")
100 """Show the list of mapped resources.
102 @return: a dictionary with 'name' and 'uri' keys for each of them.
106 for handler in CONNECTOR.values():
107 m = cls._ROOT_PATTERN.match(handler.__name__)
111 rootlist.append(name)
113 return baserlib.BuildUriList(rootlist, "/%s")
116 def _getResources(id_):
117 """Return a list of resources underneath given id.
119 This is to generalize querying of version resources lists.
121 @return: a list of resources names.
124 r_pattern = re.compile('^R_%s_([a-zA-Z0-9]+)$' % id_)
127 for handler in CONNECTOR.values():
128 m = r_pattern.match(handler.__name__)
136 class R_2(baserlib.R_Generic):
139 This is the root of the version 2 API.
144 """Show the list of mapped resources.
146 @return: a dictionary with 'name' and 'uri' keys for each of them.
149 return baserlib.BuildUriList(_getResources("2"), "/2/%s")
152 def GetHandlers(node_name_pattern, instance_name_pattern,
153 group_name_pattern, job_id_pattern, disk_pattern,
155 """Returns all supported resources and their handlers.
158 # Important note: New resources should always be added under /2. During a
159 # discussion in July 2010 it was decided that having per-resource versions
160 # is more flexible and future-compatible than versioning the whole remote
165 "/version": rlib2.R_version,
169 "/2/nodes": rlib2.R_2_nodes,
170 re.compile(r'^/2/nodes/(%s)$' % node_name_pattern):
171 rlib2.R_2_nodes_name,
172 re.compile(r'^/2/nodes/(%s)/tags$' % node_name_pattern):
173 rlib2.R_2_nodes_name_tags,
174 re.compile(r'^/2/nodes/(%s)/role$' % node_name_pattern):
175 rlib2.R_2_nodes_name_role,
176 re.compile(r'^/2/nodes/(%s)/evacuate$' % node_name_pattern):
177 rlib2.R_2_nodes_name_evacuate,
178 re.compile(r'^/2/nodes/(%s)/migrate$' % node_name_pattern):
179 rlib2.R_2_nodes_name_migrate,
180 re.compile(r'^/2/nodes/(%s)/storage$' % node_name_pattern):
181 rlib2.R_2_nodes_name_storage,
182 re.compile(r'^/2/nodes/(%s)/storage/modify$' % node_name_pattern):
183 rlib2.R_2_nodes_name_storage_modify,
184 re.compile(r'^/2/nodes/(%s)/storage/repair$' % node_name_pattern):
185 rlib2.R_2_nodes_name_storage_repair,
187 "/2/instances": rlib2.R_2_instances,
188 re.compile(r'^/2/instances/(%s)$' % instance_name_pattern):
189 rlib2.R_2_instances_name,
190 re.compile(r'^/2/instances/(%s)/info$' % instance_name_pattern):
191 rlib2.R_2_instances_name_info,
192 re.compile(r'^/2/instances/(%s)/tags$' % instance_name_pattern):
193 rlib2.R_2_instances_name_tags,
194 re.compile(r'^/2/instances/(%s)/reboot$' % instance_name_pattern):
195 rlib2.R_2_instances_name_reboot,
196 re.compile(r'^/2/instances/(%s)/reinstall$' % instance_name_pattern):
197 rlib2.R_2_instances_name_reinstall,
198 re.compile(r'^/2/instances/(%s)/replace-disks$' % instance_name_pattern):
199 rlib2.R_2_instances_name_replace_disks,
200 re.compile(r'^/2/instances/(%s)/shutdown$' % instance_name_pattern):
201 rlib2.R_2_instances_name_shutdown,
202 re.compile(r'^/2/instances/(%s)/startup$' % instance_name_pattern):
203 rlib2.R_2_instances_name_startup,
204 re.compile(r'^/2/instances/(%s)/activate-disks$' % instance_name_pattern):
205 rlib2.R_2_instances_name_activate_disks,
206 re.compile(r'^/2/instances/(%s)/deactivate-disks$' % instance_name_pattern):
207 rlib2.R_2_instances_name_deactivate_disks,
208 re.compile(r'^/2/instances/(%s)/prepare-export$' % instance_name_pattern):
209 rlib2.R_2_instances_name_prepare_export,
210 re.compile(r'^/2/instances/(%s)/export$' % instance_name_pattern):
211 rlib2.R_2_instances_name_export,
212 re.compile(r'^/2/instances/(%s)/migrate$' % instance_name_pattern):
213 rlib2.R_2_instances_name_migrate,
214 re.compile(r'^/2/instances/(%s)/rename$' % instance_name_pattern):
215 rlib2.R_2_instances_name_rename,
216 re.compile(r'^/2/instances/(%s)/modify$' % instance_name_pattern):
217 rlib2.R_2_instances_name_modify,
218 re.compile(r"^/2/instances/(%s)/disk/(%s)/grow$" %
219 (instance_name_pattern, disk_pattern)):
220 rlib2.R_2_instances_name_disk_grow,
221 re.compile(r'^/2/instances/(%s)/console$' % instance_name_pattern):
222 rlib2.R_2_instances_name_console,
224 "/2/groups": rlib2.R_2_groups,
225 re.compile(r'^/2/groups/(%s)$' % group_name_pattern):
226 rlib2.R_2_groups_name,
227 re.compile(r'^/2/groups/(%s)/modify$' % group_name_pattern):
228 rlib2.R_2_groups_name_modify,
229 re.compile(r'^/2/groups/(%s)/rename$' % group_name_pattern):
230 rlib2.R_2_groups_name_rename,
231 re.compile(r'^/2/groups/(%s)/assign-nodes$' % group_name_pattern):
232 rlib2.R_2_groups_name_assign_nodes,
234 "/2/jobs": rlib2.R_2_jobs,
235 re.compile(r"^/2/jobs/(%s)$" % job_id_pattern):
237 re.compile(r"^/2/jobs/(%s)/wait$" % job_id_pattern):
238 rlib2.R_2_jobs_id_wait,
240 "/2/tags": rlib2.R_2_tags,
241 "/2/info": rlib2.R_2_info,
242 "/2/os": rlib2.R_2_os,
243 "/2/redistribute-config": rlib2.R_2_redist_config,
244 "/2/features": rlib2.R_2_features,
245 "/2/modify": rlib2.R_2_cluster_modify,
246 re.compile(r"^/2/query/(%s)$" % query_res_pattern): rlib2.R_2_query,
247 re.compile(r"^/2/query/(%s)/fields$" % query_res_pattern):
248 rlib2.R_2_query_fields,
252 CONNECTOR.update(GetHandlers(_NAME_PATTERN, _NAME_PATTERN, _NAME_PATTERN,
253 constants.JOB_ID_TEMPLATE, _DISK_PATTERN,