connector.py 6.83 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#
#

# Copyright (C) 2006, 2007, 2008 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

"""Remote API connection map.

"""

Iustin Pop's avatar
Iustin Pop committed
25
26
27
28
# pylint: disable-msg=C0103

# C0103: Invalid name, since the R_* names are not conforming

29
30
31
import cgi
import re

Michael Hanselmann's avatar
Michael Hanselmann committed
32
33
from ganeti import constants
from ganeti import http
34
from ganeti import utils
35

Michael Hanselmann's avatar
Michael Hanselmann committed
36
from ganeti.rapi import baserlib
37
38
from ganeti.rapi import rlib2

39
40
41

_NAME_PATTERN = r"[\w\._-]+"

Michael Hanselmann's avatar
Michael Hanselmann committed
42
# the connection map is created at the end of this file
43
44
45
46
47
48
49
CONNECTOR = {}


class Mapper:
  """Map resource to method.

  """
50
  def __init__(self, connector=None):
51
52
    """Resource mapper constructor.

Iustin Pop's avatar
Iustin Pop committed
53
    @param connector: a dictionary, mapping method name with URL path regexp
54
55

    """
56
57
    if connector is None:
      connector = CONNECTOR
58
59
60
61
62
    self._connector = connector

  def getController(self, uri):
    """Find method for a given URI.

Iustin Pop's avatar
Iustin Pop committed
63
    @param uri: string with URI
64

Iustin Pop's avatar
Iustin Pop committed
65
66
67
68
69
    @return: None if no method is found or a tuple containing
        the following fields:
            - method: name of method mapped to URI
            - items: a list of variable intems in the path
            - args: a dictionary with additional parameters from URL
70
71
72
73
74
75
76
77
78
79

    """
    if '?' in uri:
      (path, query) = uri.split('?', 1)
      args = cgi.parse_qs(query)
    else:
      path = uri
      query = None
      args = {}

80
81
    # Try to find handler for request path
    result = utils.FindMatch(self._connector, path)
82

83
84
    if result is None:
      raise http.HttpNotFound()
85

86
    (handler, groups) = result
87

88
    return (handler, groups, args)
89
90
91
92
93
94


class R_root(baserlib.R_Generic):
  """/ resource.

  """
95
96
97
98
  _ROOT_PATTERN = re.compile("^R_([a-zA-Z0-9]+)$")

  @classmethod
  def GET(cls):
99
    """Show the list of mapped resources.
Michael Hanselmann's avatar
Michael Hanselmann committed
100

Iustin Pop's avatar
Iustin Pop committed
101
    @return: a dictionary with 'name' and 'uri' keys for each of them.
102
103
104
105

    """
    rootlist = []
    for handler in CONNECTOR.values():
106
      m = cls._ROOT_PATTERN.match(handler.__name__)
107
108
109
110
111
112
113
114
      if m:
        name = m.group(1)
        if name != 'root':
          rootlist.append(name)

    return baserlib.BuildUriList(rootlist, "/%s")


115
def _getResources(id_):
116
117
118
119
120
121
122
  """Return a list of resources underneath given id.

  This is to generalize querying of version resources lists.

  @return: a list of resources names.

  """
123
  r_pattern = re.compile('^R_%s_([a-zA-Z0-9]+)$' % id_)
124
125
126
127
128
129
130
131
132
133
134
135

  rlist = []
  for handler in CONNECTOR.values():
    m = r_pattern.match(handler.__name__)
    if m:
      name = m.group(1)
      rlist.append(name)

  return rlist


class R_2(baserlib.R_Generic):
Iustin Pop's avatar
Iustin Pop committed
136
  """ /2 resource, the root of the version 2 API.
137
138

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

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

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


149
150
151
152
def GetHandlers(node_name_pattern, instance_name_pattern, job_id_pattern):
  """Returns all supported resources and their handlers.

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

    "/version": rlib2.R_version,
161

162
    "/2": R_2,
163

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

    "/2/instances": rlib2.R_2_instances,
    re.compile(r'^/2/instances/(%s)$' % instance_name_pattern):
      rlib2.R_2_instances_name,
    re.compile(r'^/2/instances/(%s)/info$' % instance_name_pattern):
186
      rlib2.R_2_instances_name_info,
187
188
189
    re.compile(r'^/2/instances/(%s)/tags$' % instance_name_pattern):
      rlib2.R_2_instances_name_tags,
    re.compile(r'^/2/instances/(%s)/reboot$' % instance_name_pattern):
190
      rlib2.R_2_instances_name_reboot,
191
    re.compile(r'^/2/instances/(%s)/reinstall$' % instance_name_pattern):
192
      rlib2.R_2_instances_name_reinstall,
193
    re.compile(r'^/2/instances/(%s)/replace-disks$' % instance_name_pattern):
194
      rlib2.R_2_instances_name_replace_disks,
195
    re.compile(r'^/2/instances/(%s)/shutdown$' % instance_name_pattern):
196
      rlib2.R_2_instances_name_shutdown,
197
    re.compile(r'^/2/instances/(%s)/startup$' % instance_name_pattern):
198
      rlib2.R_2_instances_name_startup,
199
200
    re.compile(r'^/2/instances/(%s)/activate-disks$' % instance_name_pattern):
      rlib2.R_2_instances_name_activate_disks,
201
202
    re.compile(r'^/2/instances/(%s)/deactivate-disks$' % instance_name_pattern):
      rlib2.R_2_instances_name_deactivate_disks,
203
204
205
206
    re.compile(r'^/2/instances/(%s)/prepare-export$' % instance_name_pattern):
      rlib2.R_2_instances_name_prepare_export,
    re.compile(r'^/2/instances/(%s)/export$' % instance_name_pattern):
      rlib2.R_2_instances_name_export,
207
208
    re.compile(r'^/2/instances/(%s)/migrate$' % instance_name_pattern):
      rlib2.R_2_instances_name_migrate,
209
210
    re.compile(r'^/2/instances/(%s)/rename$' % instance_name_pattern):
      rlib2.R_2_instances_name_rename,
211
212
    re.compile(r'^/2/instances/(%s)/modify$' % instance_name_pattern):
      rlib2.R_2_instances_name_modify,
213
214

    "/2/jobs": rlib2.R_2_jobs,
215
    re.compile(r"^/2/jobs/(%s)$" % job_id_pattern):
216
      rlib2.R_2_jobs_id,
217
    re.compile(r"^/2/jobs/(%s)/wait$" % job_id_pattern):
218
      rlib2.R_2_jobs_id_wait,
219
220
221
222

    "/2/tags": rlib2.R_2_tags,
    "/2/info": rlib2.R_2_info,
    "/2/os": rlib2.R_2_os,
223
    "/2/redistribute-config": rlib2.R_2_redist_config,
224
    "/2/features": rlib2.R_2_features,
225
226
227
228
229
    }


CONNECTOR.update(GetHandlers(_NAME_PATTERN, _NAME_PATTERN,
                             constants.JOB_ID_TEMPLATE))