Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / connector.py @ c0a146a1

History | View | Annotate | Download (8.1 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
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.
138

139
  This is the root of the version 2 API.
140

141
  """
142
  @staticmethod
143
  def GET():
144
    """Show the list of mapped resources.
145

146
    @return: a dictionary with 'name' and 'uri' keys for each of them.
147

148
    """
149
    return baserlib.BuildUriList(_getResources("2"), "/2/%s")
150

    
151

    
152
def GetHandlers(node_name_pattern, instance_name_pattern,
153
                group_name_pattern, job_id_pattern, disk_pattern,
154
                query_res_pattern):
155
  """Returns all supported resources and their handlers.
156

157
  """
158
  # Important note: New resources should always be added under /2. During a
159
  # discussion in July 2010 it was decided that having per-resource versions
160
  # is more flexible and future-compatible than versioning the whole remote
161
  # API.
162
  return {
163
    "/": R_root,
164

    
165
    "/version": rlib2.R_version,
166

    
167
    "/2": R_2,
168

    
169
    "/2/nodes": rlib2.R_2_nodes,
170
    re.compile(r'^/2/nodes/(%s)$' % node_name_pattern):
171
      rlib2.R_2_nodes_name,
172
    re.compile(r'^/2/nodes/(%s)/tags$' % node_name_pattern):
173
      rlib2.R_2_nodes_name_tags,
174
    re.compile(r'^/2/nodes/(%s)/role$' % node_name_pattern):
175
      rlib2.R_2_nodes_name_role,
176
    re.compile(r'^/2/nodes/(%s)/evacuate$' % node_name_pattern):
177
      rlib2.R_2_nodes_name_evacuate,
178
    re.compile(r'^/2/nodes/(%s)/migrate$' % node_name_pattern):
179
      rlib2.R_2_nodes_name_migrate,
180
    re.compile(r'^/2/nodes/(%s)/storage$' % node_name_pattern):
181
      rlib2.R_2_nodes_name_storage,
182
    re.compile(r'^/2/nodes/(%s)/storage/modify$' % node_name_pattern):
183
      rlib2.R_2_nodes_name_storage_modify,
184
    re.compile(r'^/2/nodes/(%s)/storage/repair$' % node_name_pattern):
185
      rlib2.R_2_nodes_name_storage_repair,
186

    
187
    "/2/instances": rlib2.R_2_instances,
188
    re.compile(r'^/2/instances/(%s)$' % instance_name_pattern):
189
      rlib2.R_2_instances_name,
190
    re.compile(r'^/2/instances/(%s)/info$' % instance_name_pattern):
191
      rlib2.R_2_instances_name_info,
192
    re.compile(r'^/2/instances/(%s)/tags$' % instance_name_pattern):
193
      rlib2.R_2_instances_name_tags,
194
    re.compile(r'^/2/instances/(%s)/reboot$' % instance_name_pattern):
195
      rlib2.R_2_instances_name_reboot,
196
    re.compile(r'^/2/instances/(%s)/reinstall$' % instance_name_pattern):
197
      rlib2.R_2_instances_name_reinstall,
198
    re.compile(r'^/2/instances/(%s)/replace-disks$' % instance_name_pattern):
199
      rlib2.R_2_instances_name_replace_disks,
200
    re.compile(r'^/2/instances/(%s)/shutdown$' % instance_name_pattern):
201
      rlib2.R_2_instances_name_shutdown,
202
    re.compile(r'^/2/instances/(%s)/startup$' % instance_name_pattern):
203
      rlib2.R_2_instances_name_startup,
204
    re.compile(r'^/2/instances/(%s)/activate-disks$' % instance_name_pattern):
205
      rlib2.R_2_instances_name_activate_disks,
206
    re.compile(r'^/2/instances/(%s)/deactivate-disks$' % instance_name_pattern):
207
      rlib2.R_2_instances_name_deactivate_disks,
208
    re.compile(r'^/2/instances/(%s)/prepare-export$' % instance_name_pattern):
209
      rlib2.R_2_instances_name_prepare_export,
210
    re.compile(r'^/2/instances/(%s)/export$' % instance_name_pattern):
211
      rlib2.R_2_instances_name_export,
212
    re.compile(r'^/2/instances/(%s)/migrate$' % instance_name_pattern):
213
      rlib2.R_2_instances_name_migrate,
214
    re.compile(r'^/2/instances/(%s)/failover$' % instance_name_pattern):
215
      rlib2.R_2_instances_name_failover,
216
    re.compile(r'^/2/instances/(%s)/rename$' % instance_name_pattern):
217
      rlib2.R_2_instances_name_rename,
218
    re.compile(r'^/2/instances/(%s)/modify$' % instance_name_pattern):
219
      rlib2.R_2_instances_name_modify,
220
    re.compile(r"^/2/instances/(%s)/disk/(%s)/grow$" %
221
               (instance_name_pattern, disk_pattern)):
222
      rlib2.R_2_instances_name_disk_grow,
223
    re.compile(r'^/2/instances/(%s)/console$' % instance_name_pattern):
224
      rlib2.R_2_instances_name_console,
225

    
226
    "/2/groups": rlib2.R_2_groups,
227
    re.compile(r'^/2/groups/(%s)$' % group_name_pattern):
228
      rlib2.R_2_groups_name,
229
    re.compile(r'^/2/groups/(%s)/modify$' % group_name_pattern):
230
      rlib2.R_2_groups_name_modify,
231
    re.compile(r'^/2/groups/(%s)/rename$' % group_name_pattern):
232
      rlib2.R_2_groups_name_rename,
233
    re.compile(r'^/2/groups/(%s)/assign-nodes$' % group_name_pattern):
234
      rlib2.R_2_groups_name_assign_nodes,
235
    re.compile(r'^/2/groups/(%s)/tags$' % group_name_pattern):
236
      rlib2.R_2_groups_name_tags,
237

    
238
    "/2/jobs": rlib2.R_2_jobs,
239
    re.compile(r"^/2/jobs/(%s)$" % job_id_pattern):
240
      rlib2.R_2_jobs_id,
241
    re.compile(r"^/2/jobs/(%s)/wait$" % job_id_pattern):
242
      rlib2.R_2_jobs_id_wait,
243

    
244
    "/2/tags": rlib2.R_2_tags,
245
    "/2/info": rlib2.R_2_info,
246
    "/2/os": rlib2.R_2_os,
247
    "/2/redistribute-config": rlib2.R_2_redist_config,
248
    "/2/features": rlib2.R_2_features,
249
    "/2/modify": rlib2.R_2_cluster_modify,
250
    re.compile(r"^/2/query/(%s)$" % query_res_pattern): rlib2.R_2_query,
251
    re.compile(r"^/2/query/(%s)/fields$" % query_res_pattern):
252
      rlib2.R_2_query_fields,
253
    }
254

    
255

    
256
CONNECTOR.update(GetHandlers(_NAME_PATTERN, _NAME_PATTERN, _NAME_PATTERN,
257
                             constants.JOB_ID_TEMPLATE, _DISK_PATTERN,
258
                             _NAME_PATTERN))