RAPI: Add resource to grow instance disk
[ganeti-local] / lib / rapi / connector.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008 Google Inc.
5 #
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.
10 #
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.
15 #
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
19 # 02110-1301, USA.
20
21 """Remote API connection map.
22
23 """
24
25 # pylint: disable-msg=C0103
26
27 # C0103: Invalid name, since the R_* names are not conforming
28
29 import cgi
30 import re
31
32 from ganeti import constants
33 from ganeti import http
34 from ganeti import utils
35
36 from ganeti.rapi import baserlib
37 from ganeti.rapi import rlib2
38
39
40 _NAME_PATTERN = r"[\w\._-]+"
41 _DISK_PATTERN = r"\d+"
42
43 # the connection map is created at the end of this file
44 CONNECTOR = {}
45
46
47 class Mapper:
48   """Map resource to method.
49
50   """
51   def __init__(self, connector=None):
52     """Resource mapper constructor.
53
54     @param connector: a dictionary, mapping method name with URL path regexp
55
56     """
57     if connector is None:
58       connector = CONNECTOR
59     self._connector = connector
60
61   def getController(self, uri):
62     """Find method for a given URI.
63
64     @param uri: string with URI
65
66     @return: None if no method is found or a tuple containing
67         the following fields:
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
71
72     """
73     if '?' in uri:
74       (path, query) = uri.split('?', 1)
75       args = cgi.parse_qs(query)
76     else:
77       path = uri
78       query = None
79       args = {}
80
81     # Try to find handler for request path
82     result = utils.FindMatch(self._connector, path)
83
84     if result is None:
85       raise http.HttpNotFound()
86
87     (handler, groups) = result
88
89     return (handler, groups, args)
90
91
92 class R_root(baserlib.R_Generic):
93   """/ resource.
94
95   """
96   _ROOT_PATTERN = re.compile("^R_([a-zA-Z0-9]+)$")
97
98   @classmethod
99   def GET(cls):
100     """Show the list of mapped resources.
101
102     @return: a dictionary with 'name' and 'uri' keys for each of them.
103
104     """
105     rootlist = []
106     for handler in CONNECTOR.values():
107       m = cls._ROOT_PATTERN.match(handler.__name__)
108       if m:
109         name = m.group(1)
110         if name != 'root':
111           rootlist.append(name)
112
113     return baserlib.BuildUriList(rootlist, "/%s")
114
115
116 def _getResources(id_):
117   """Return a list of resources underneath given id.
118
119   This is to generalize querying of version resources lists.
120
121   @return: a list of resources names.
122
123   """
124   r_pattern = re.compile('^R_%s_([a-zA-Z0-9]+)$' % id_)
125
126   rlist = []
127   for handler in CONNECTOR.values():
128     m = r_pattern.match(handler.__name__)
129     if m:
130       name = m.group(1)
131       rlist.append(name)
132
133   return rlist
134
135
136 class R_2(baserlib.R_Generic):
137   """ /2 resource, the root of the version 2 API.
138
139   """
140   @staticmethod
141   def GET():
142     """Show the list of mapped resources.
143
144     @return: a dictionary with 'name' and 'uri' keys for each of them.
145
146     """
147     return baserlib.BuildUriList(_getResources("2"), "/2/%s")
148
149
150 def GetHandlers(node_name_pattern, instance_name_pattern,
151                 group_name_pattern, job_id_pattern, disk_pattern):
152   """Returns all supported resources and their handlers.
153
154   """
155   # Important note: New resources should always be added under /2. During a
156   # discussion in July 2010 it was decided that having per-resource versions
157   # is more flexible and future-compatible than versioning the whole remote
158   # API.
159   return {
160     "/": R_root,
161
162     "/version": rlib2.R_version,
163
164     "/2": R_2,
165
166     "/2/nodes": rlib2.R_2_nodes,
167     re.compile(r'^/2/nodes/(%s)$' % node_name_pattern):
168       rlib2.R_2_nodes_name,
169     re.compile(r'^/2/nodes/(%s)/tags$' % node_name_pattern):
170       rlib2.R_2_nodes_name_tags,
171     re.compile(r'^/2/nodes/(%s)/role$' % node_name_pattern):
172       rlib2.R_2_nodes_name_role,
173     re.compile(r'^/2/nodes/(%s)/evacuate$' % node_name_pattern):
174       rlib2.R_2_nodes_name_evacuate,
175     re.compile(r'^/2/nodes/(%s)/migrate$' % node_name_pattern):
176       rlib2.R_2_nodes_name_migrate,
177     re.compile(r'^/2/nodes/(%s)/storage$' % node_name_pattern):
178       rlib2.R_2_nodes_name_storage,
179     re.compile(r'^/2/nodes/(%s)/storage/modify$' % node_name_pattern):
180       rlib2.R_2_nodes_name_storage_modify,
181     re.compile(r'^/2/nodes/(%s)/storage/repair$' % node_name_pattern):
182       rlib2.R_2_nodes_name_storage_repair,
183
184     "/2/instances": rlib2.R_2_instances,
185     re.compile(r'^/2/instances/(%s)$' % instance_name_pattern):
186       rlib2.R_2_instances_name,
187     re.compile(r'^/2/instances/(%s)/info$' % instance_name_pattern):
188       rlib2.R_2_instances_name_info,
189     re.compile(r'^/2/instances/(%s)/tags$' % instance_name_pattern):
190       rlib2.R_2_instances_name_tags,
191     re.compile(r'^/2/instances/(%s)/reboot$' % instance_name_pattern):
192       rlib2.R_2_instances_name_reboot,
193     re.compile(r'^/2/instances/(%s)/reinstall$' % instance_name_pattern):
194       rlib2.R_2_instances_name_reinstall,
195     re.compile(r'^/2/instances/(%s)/replace-disks$' % instance_name_pattern):
196       rlib2.R_2_instances_name_replace_disks,
197     re.compile(r'^/2/instances/(%s)/shutdown$' % instance_name_pattern):
198       rlib2.R_2_instances_name_shutdown,
199     re.compile(r'^/2/instances/(%s)/startup$' % instance_name_pattern):
200       rlib2.R_2_instances_name_startup,
201     re.compile(r'^/2/instances/(%s)/activate-disks$' % instance_name_pattern):
202       rlib2.R_2_instances_name_activate_disks,
203     re.compile(r'^/2/instances/(%s)/deactivate-disks$' % instance_name_pattern):
204       rlib2.R_2_instances_name_deactivate_disks,
205     re.compile(r'^/2/instances/(%s)/prepare-export$' % instance_name_pattern):
206       rlib2.R_2_instances_name_prepare_export,
207     re.compile(r'^/2/instances/(%s)/export$' % instance_name_pattern):
208       rlib2.R_2_instances_name_export,
209     re.compile(r'^/2/instances/(%s)/migrate$' % instance_name_pattern):
210       rlib2.R_2_instances_name_migrate,
211     re.compile(r'^/2/instances/(%s)/rename$' % instance_name_pattern):
212       rlib2.R_2_instances_name_rename,
213     re.compile(r'^/2/instances/(%s)/modify$' % instance_name_pattern):
214       rlib2.R_2_instances_name_modify,
215     re.compile(r"^/2/instances/(%s)/disk/(%s)/grow$" %
216                (instance_name_pattern, disk_pattern)):
217       rlib2.R_2_instances_name_disk_grow,
218
219     "/2/groups": rlib2.R_2_groups,
220     re.compile(r'^/2/groups/(%s)$' % group_name_pattern):
221       rlib2.R_2_groups_name,
222     re.compile(r'^/2/groups/(%s)/modify$' % group_name_pattern):
223       rlib2.R_2_groups_name_modify,
224     re.compile(r'^/2/groups/(%s)/rename$' % group_name_pattern):
225       rlib2.R_2_groups_name_rename,
226
227     "/2/jobs": rlib2.R_2_jobs,
228     re.compile(r"^/2/jobs/(%s)$" % job_id_pattern):
229       rlib2.R_2_jobs_id,
230     re.compile(r"^/2/jobs/(%s)/wait$" % job_id_pattern):
231       rlib2.R_2_jobs_id_wait,
232
233     "/2/tags": rlib2.R_2_tags,
234     "/2/info": rlib2.R_2_info,
235     "/2/os": rlib2.R_2_os,
236     "/2/redistribute-config": rlib2.R_2_redist_config,
237     "/2/features": rlib2.R_2_features,
238     "/2/modify": rlib2.R_2_cluster_modify,
239     }
240
241
242 CONNECTOR.update(GetHandlers(_NAME_PATTERN, _NAME_PATTERN, _NAME_PATTERN,
243                              constants.JOB_ID_TEMPLATE, _DISK_PATTERN))