rapi: Implement /2/nodes/[node_name]/role resource
[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 import cgi
26 import re
27
28 from ganeti import constants
29 from ganeti import http
30
31 from ganeti.rapi import baserlib
32 from ganeti.rapi import rlib2
33
34 # the connection map is created at the end of this file
35 CONNECTOR = {}
36
37
38 class Mapper:
39   """Map resource to method.
40
41   """
42   def __init__(self, connector=CONNECTOR):
43     """Resource mapper constructor.
44
45     @param connector: a dictionary, mapping method name with URL path regexp
46
47     """
48     self._connector = connector
49
50   def getController(self, uri):
51     """Find method for a given URI.
52
53     @param uri: string with URI
54
55     @return: None if no method is found or a tuple containing
56         the following fields:
57             - method: name of method mapped to URI
58             - items: a list of variable intems in the path
59             - args: a dictionary with additional parameters from URL
60
61     """
62     if '?' in uri:
63       (path, query) = uri.split('?', 1)
64       args = cgi.parse_qs(query)
65     else:
66       path = uri
67       query = None
68       args = {}
69
70     result = None
71
72     for key, handler in self._connector.iteritems():
73       # Regex objects
74       if hasattr(key, "match"):
75         m = key.match(path)
76         if m:
77           result = (handler, list(m.groups()), args)
78           break
79
80       # String objects
81       elif key == path:
82         result = (handler, [], args)
83         break
84
85     if result:
86       return result
87     else:
88       raise http.HttpNotFound()
89
90
91 class R_root(baserlib.R_Generic):
92   """/ resource.
93
94   """
95   def GET(self):
96     """Show the list of mapped resources.
97
98     @return: a dictionary with 'name' and 'uri' keys for each of them.
99
100     """
101     root_pattern = re.compile('^R_([a-zA-Z0-9]+)$')
102
103     rootlist = []
104     for handler in CONNECTOR.values():
105       m = root_pattern.match(handler.__name__)
106       if m:
107         name = m.group(1)
108         if name != 'root':
109           rootlist.append(name)
110
111     return baserlib.BuildUriList(rootlist, "/%s")
112
113
114 def _getResources(id):
115   """Return a list of resources underneath given id.
116
117   This is to generalize querying of version resources lists.
118
119   @return: a list of resources names.
120
121   """
122   r_pattern = re.compile('^R_%s_([a-zA-Z0-9]+)$' % id)
123
124   rlist = []
125   for handler in CONNECTOR.values():
126     m = r_pattern.match(handler.__name__)
127     if m:
128       name = m.group(1)
129       rlist.append(name)
130
131   return rlist
132
133
134 class R_2(baserlib.R_Generic):
135   """ /2 resource, the root of the version 2 API.
136
137   """
138   def GET(self):
139     """Show the list of mapped resources.
140
141     @return: a dictionary with 'name' and 'uri' keys for each of them.
142
143     """
144     return baserlib.BuildUriList(_getResources("2"), "/2/%s")
145
146
147 CONNECTOR.update({
148   "/": R_root,
149
150   "/version": rlib2.R_version,
151
152   "/2": R_2,
153   "/2/jobs": rlib2.R_2_jobs,
154   "/2/nodes": rlib2.R_2_nodes,
155   re.compile(r'^/2/nodes/([\w\._-]+)$'): rlib2.R_2_nodes_name,
156   re.compile(r'^/2/nodes/([\w\._-]+)/tags$'): rlib2.R_2_nodes_name_tags,
157   re.compile(r'^/2/nodes/([\w\._-]+)/role$'): rlib2.R_2_nodes_name_role,
158   "/2/instances": rlib2.R_2_instances,
159   re.compile(r'^/2/instances/([\w\._-]+)$'): rlib2.R_2_instances_name,
160   re.compile(r'^/2/instances/([\w\._-]+)/tags$'): rlib2.R_2_instances_name_tags,
161   re.compile(r'^/2/instances/([\w\._-]+)/reboot$'):
162       rlib2.R_2_instances_name_reboot,
163   re.compile(r'^/2/instances/([\w\._-]+)/shutdown$'):
164       rlib2.R_2_instances_name_shutdown,
165   re.compile(r'^/2/instances/([\w\._-]+)/startup$'):
166       rlib2.R_2_instances_name_startup,
167   re.compile(r'/2/jobs/(%s)$' % constants.JOB_ID_TEMPLATE): rlib2.R_2_jobs_id,
168   "/2/tags": rlib2.R_2_tags,
169   "/2/info": rlib2.R_2_info,
170   "/2/os": rlib2.R_2_os,
171   })