Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / connector.py @ 3882937a

History | View | Annotate | Download (7 kB)

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))