RAPI: documentation updates
[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   DOC_URI = "/"
96
97   def GET(self):
98     """Show the list of mapped resources.
99
100     @return: a dictionary with 'name' and 'uri' keys for each of them.
101
102     """
103     root_pattern = re.compile('^R_([a-zA-Z0-9]+)$')
104
105     rootlist = []
106     for handler in CONNECTOR.values():
107       m = 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   DOC_URI = "/2"
141
142   def GET(self):
143     """Show the list of mapped resources.
144
145     @return: a dictionary with 'name' and 'uri' keys for each of them.
146
147     """
148     return baserlib.BuildUriList(_getResources("2"), "/2/%s")
149
150
151 CONNECTOR.update({
152   "/": R_root,
153
154   "/version": rlib2.R_version,
155
156   "/2": R_2,
157   "/2/jobs": rlib2.R_2_jobs,
158   "/2/nodes": rlib2.R_2_nodes,
159   re.compile(r'^/2/nodes/([\w\._-]+)$'): rlib2.R_2_nodes_name,
160   re.compile(r'^/2/nodes/([\w\._-]+)/tags$'): rlib2.R_2_nodes_name_tags,
161   "/2/instances": rlib2.R_2_instances,
162   re.compile(r'^/2/instances/([\w\._-]+)$'): rlib2.R_2_instances_name,
163   re.compile(r'^/2/instances/([\w\._-]+)/tags$'): rlib2.R_2_instances_name_tags,
164   re.compile(r'^/2/instances/([\w\._-]+)/reboot$'):
165       rlib2.R_2_instances_name_reboot,
166   re.compile(r'^/2/instances/([\w\._-]+)/shutdown$'):
167       rlib2.R_2_instances_name_shutdown,
168   re.compile(r'^/2/instances/([\w\._-]+)/startup$'):
169       rlib2.R_2_instances_name_startup,
170   re.compile(r'/2/jobs/(%s)$' % constants.JOB_ID_TEMPLATE): rlib2.R_2_jobs_id,
171   "/2/tags": rlib2.R_2_tags,
172   "/2/info": rlib2.R_2_info,
173   "/2/os": rlib2.R_2_os,
174   })