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