Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / rlib2.py @ 1b01390b

History | View | Annotate | Download (34.2 kB)

1 10b207d4 Oleksiy Mishchenko
#
2 10b207d4 Oleksiy Mishchenko
#
3 10b207d4 Oleksiy Mishchenko
4 4ff922a2 Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Google Inc.
5 10b207d4 Oleksiy Mishchenko
#
6 10b207d4 Oleksiy Mishchenko
# This program is free software; you can redistribute it and/or modify
7 10b207d4 Oleksiy Mishchenko
# it under the terms of the GNU General Public License as published by
8 10b207d4 Oleksiy Mishchenko
# the Free Software Foundation; either version 2 of the License, or
9 10b207d4 Oleksiy Mishchenko
# (at your option) any later version.
10 10b207d4 Oleksiy Mishchenko
#
11 10b207d4 Oleksiy Mishchenko
# This program is distributed in the hope that it will be useful, but
12 10b207d4 Oleksiy Mishchenko
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 10b207d4 Oleksiy Mishchenko
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 10b207d4 Oleksiy Mishchenko
# General Public License for more details.
15 10b207d4 Oleksiy Mishchenko
#
16 10b207d4 Oleksiy Mishchenko
# You should have received a copy of the GNU General Public License
17 10b207d4 Oleksiy Mishchenko
# along with this program; if not, write to the Free Software
18 10b207d4 Oleksiy Mishchenko
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 10b207d4 Oleksiy Mishchenko
# 02110-1301, USA.
20 10b207d4 Oleksiy Mishchenko
21 10b207d4 Oleksiy Mishchenko
22 f3c1a70c Michael Hanselmann
"""Remote API resource implementations.
23 10b207d4 Oleksiy Mishchenko

24 f3c1a70c Michael Hanselmann
PUT or POST?
25 f3c1a70c Michael Hanselmann
============
26 88394aa7 René Nussbaumer

27 f3c1a70c Michael Hanselmann
According to RFC2616 the main difference between PUT and POST is that
28 f3c1a70c Michael Hanselmann
POST can create new resources but PUT can only create the resource the
29 f3c1a70c Michael Hanselmann
URI was pointing to on the PUT request.
30 88394aa7 René Nussbaumer

31 d91a933f Michael Hanselmann
In the context of this module POST on ``/2/instances`` to change an existing
32 f3c1a70c Michael Hanselmann
entity is legitimate, while PUT would not be. PUT creates a new entity (e.g. a
33 f3c1a70c Michael Hanselmann
new instance) with a name specified in the request.
34 88394aa7 René Nussbaumer

35 140ff718 Michael Hanselmann
Quoting from RFC2616, section 9.6::
36 f3c1a70c Michael Hanselmann

37 f3c1a70c Michael Hanselmann
  The fundamental difference between the POST and PUT requests is reflected in
38 f3c1a70c Michael Hanselmann
  the different meaning of the Request-URI. The URI in a POST request
39 f3c1a70c Michael Hanselmann
  identifies the resource that will handle the enclosed entity. That resource
40 f3c1a70c Michael Hanselmann
  might be a data-accepting process, a gateway to some other protocol, or a
41 f3c1a70c Michael Hanselmann
  separate entity that accepts annotations. In contrast, the URI in a PUT
42 f3c1a70c Michael Hanselmann
  request identifies the entity enclosed with the request -- the user agent
43 f3c1a70c Michael Hanselmann
  knows what URI is intended and the server MUST NOT attempt to apply the
44 f3c1a70c Michael Hanselmann
  request to some other resource. If the server desires that the request be
45 f3c1a70c Michael Hanselmann
  applied to a different URI, it MUST send a 301 (Moved Permanently) response;
46 f3c1a70c Michael Hanselmann
  the user agent MAY then make its own decision regarding whether or not to
47 f3c1a70c Michael Hanselmann
  redirect the request.
48 f3c1a70c Michael Hanselmann

49 f3c1a70c Michael Hanselmann
So when adding new methods, if they are operating on the URI entity itself,
50 f3c1a70c Michael Hanselmann
PUT should be prefered over POST.
51 88394aa7 René Nussbaumer

52 10b207d4 Oleksiy Mishchenko
"""
53 10b207d4 Oleksiy Mishchenko
54 b459a848 Andrea Spadaccini
# pylint: disable=C0103
55 fe267188 Iustin Pop
56 fe267188 Iustin Pop
# C0103: Invalid name, since the R_* names are not conforming
57 fe267188 Iustin Pop
58 59b4eeef Iustin Pop
from ganeti import opcodes
59 15fd9fd5 Oleksiy Mishchenko
from ganeti import http
60 15fd9fd5 Oleksiy Mishchenko
from ganeti import constants
61 59b4eeef Iustin Pop
from ganeti import cli
62 8381fa2d Michael Hanselmann
from ganeti import rapi
63 d1c172de Michael Hanselmann
from ganeti import ht
64 e987f166 Michael Hanselmann
from ganeti import compat
65 be1ddd09 Michael Hanselmann
from ganeti import ssconf
66 38206f3c Iustin Pop
from ganeti.rapi import baserlib
67 10b207d4 Oleksiy Mishchenko
68 4e5a68f8 Oleksiy Mishchenko
69 7118a0df Iustin Pop
_COMMON_FIELDS = ["ctime", "mtime", "uuid", "serial_no", "tags"]
70 9031ee8e Iustin Pop
I_FIELDS = ["name", "admin_state", "os",
71 9031ee8e Iustin Pop
            "pnode", "snodes",
72 9031ee8e Iustin Pop
            "disk_template",
73 82e186f8 Michael Hanselmann
            "nic.ips", "nic.macs", "nic.modes", "nic.links", "nic.bridges",
74 a8b16c4e Tim Boring
            "network_port",
75 024e157f Iustin Pop
            "disk.sizes", "disk_usage",
76 a5b9d725 Iustin Pop
            "beparams", "hvparams",
77 4ea3de4e Balazs Lecz
            "oper_state", "oper_ram", "oper_vcpus", "status",
78 90224407 Iustin Pop
            "custom_hvparams", "custom_beparams", "custom_nicparams",
79 7118a0df Iustin Pop
            ] + _COMMON_FIELDS
80 9031ee8e Iustin Pop
81 0b2454b9 Iustin Pop
N_FIELDS = ["name", "offline", "master_candidate", "drained",
82 9031ee8e Iustin Pop
            "dtotal", "dfree",
83 4e5a68f8 Oleksiy Mishchenko
            "mtotal", "mnode", "mfree",
84 7118a0df Iustin Pop
            "pinst_cnt", "sinst_cnt",
85 0105bad3 Iustin Pop
            "ctotal", "cnodes", "csockets",
86 7118a0df Iustin Pop
            "pip", "sip", "role",
87 93962b80 Iustin Pop
            "pinst_list", "sinst_list",
88 c190e817 Iustin Pop
            "master_capable", "vm_capable",
89 fd254195 Iustin Pop
            "group.uuid",
90 7118a0df Iustin Pop
            ] + _COMMON_FIELDS
91 4e5a68f8 Oleksiy Mishchenko
92 f4e86448 Michael Hanselmann
G_FIELDS = [
93 f4e86448 Michael Hanselmann
  "alloc_policy",
94 f4e86448 Michael Hanselmann
  "name",
95 f4e86448 Michael Hanselmann
  "node_cnt",
96 f4e86448 Michael Hanselmann
  "node_list",
97 edd49f9b Agata Murawska
  "ipolicy",
98 f4e86448 Michael Hanselmann
  ] + _COMMON_FIELDS
99 0897dc97 Adeodato Simo
100 bd7b2070 Michael Hanselmann
J_FIELDS_BULK = [
101 e987f166 Michael Hanselmann
  "id", "ops", "status", "summary",
102 bd7b2070 Michael Hanselmann
  "opstatus",
103 e987f166 Michael Hanselmann
  "received_ts", "start_ts", "end_ts",
104 e987f166 Michael Hanselmann
  ]
105 e987f166 Michael Hanselmann
106 bd7b2070 Michael Hanselmann
J_FIELDS = J_FIELDS_BULK + [
107 bd7b2070 Michael Hanselmann
  "oplog",
108 bd7b2070 Michael Hanselmann
  "opresult",
109 bd7b2070 Michael Hanselmann
  ]
110 bd7b2070 Michael Hanselmann
111 64dae8fc Michael Hanselmann
_NR_DRAINED = "drained"
112 51cc8637 Michael Hanselmann
_NR_MASTER_CANDIDATE = "master-candidate"
113 64dae8fc Michael Hanselmann
_NR_MASTER = "master"
114 64dae8fc Michael Hanselmann
_NR_OFFLINE = "offline"
115 64dae8fc Michael Hanselmann
_NR_REGULAR = "regular"
116 64dae8fc Michael Hanselmann
117 64dae8fc Michael Hanselmann
_NR_MAP = {
118 1e28e3b8 Michael Hanselmann
  constants.NR_MASTER: _NR_MASTER,
119 51cc8637 Michael Hanselmann
  constants.NR_MCANDIDATE: _NR_MASTER_CANDIDATE,
120 1e28e3b8 Michael Hanselmann
  constants.NR_DRAINED: _NR_DRAINED,
121 1e28e3b8 Michael Hanselmann
  constants.NR_OFFLINE: _NR_OFFLINE,
122 1e28e3b8 Michael Hanselmann
  constants.NR_REGULAR: _NR_REGULAR,
123 64dae8fc Michael Hanselmann
  }
124 64dae8fc Michael Hanselmann
125 1e28e3b8 Michael Hanselmann
assert frozenset(_NR_MAP.keys()) == constants.NR_ALL
126 1e28e3b8 Michael Hanselmann
127 d975f482 Michael Hanselmann
# Request data version field
128 d975f482 Michael Hanselmann
_REQ_DATA_VERSION = "__version__"
129 d975f482 Michael Hanselmann
130 6395cebb Michael Hanselmann
# Feature string for instance creation request data version 1
131 6395cebb Michael Hanselmann
_INST_CREATE_REQV1 = "instance-create-reqv1"
132 6395cebb Michael Hanselmann
133 c744425f Michael Hanselmann
# Feature string for instance reinstall request version 1
134 c744425f Michael Hanselmann
_INST_REINSTALL_REQV1 = "instance-reinstall-reqv1"
135 c744425f Michael Hanselmann
136 b7a1c816 Michael Hanselmann
# Feature string for node migration version 1
137 b7a1c816 Michael Hanselmann
_NODE_MIGRATE_REQV1 = "node-migrate-reqv1"
138 b7a1c816 Michael Hanselmann
139 de40437a Michael Hanselmann
# Feature string for node evacuation with LU-generated jobs
140 de40437a Michael Hanselmann
_NODE_EVAC_RES1 = "node-evac-res1"
141 de40437a Michael Hanselmann
142 b4fcee5b Michael Hanselmann
ALL_FEATURES = frozenset([
143 b4fcee5b Michael Hanselmann
  _INST_CREATE_REQV1,
144 b4fcee5b Michael Hanselmann
  _INST_REINSTALL_REQV1,
145 b4fcee5b Michael Hanselmann
  _NODE_MIGRATE_REQV1,
146 b4fcee5b Michael Hanselmann
  _NODE_EVAC_RES1,
147 b4fcee5b Michael Hanselmann
  ])
148 b4fcee5b Michael Hanselmann
149 793a8f7c Michael Hanselmann
# Timeout for /2/jobs/[job_id]/wait. Gives job up to 10 seconds to change.
150 793a8f7c Michael Hanselmann
_WFJC_TIMEOUT = 10
151 793a8f7c Michael Hanselmann
152 4e5a68f8 Oleksiy Mishchenko
153 26ff6ee2 Michael Hanselmann
class R_root(baserlib.ResourceBase):
154 0f945c65 Michael Hanselmann
  """/ resource.
155 0f945c65 Michael Hanselmann

156 0f945c65 Michael Hanselmann
  """
157 0f945c65 Michael Hanselmann
  @staticmethod
158 0f945c65 Michael Hanselmann
  def GET():
159 0f945c65 Michael Hanselmann
    """Supported for legacy reasons.
160 0f945c65 Michael Hanselmann

161 0f945c65 Michael Hanselmann
    """
162 0f945c65 Michael Hanselmann
    return None
163 0f945c65 Michael Hanselmann
164 0f945c65 Michael Hanselmann
165 132cdb87 Michael Hanselmann
class R_2(R_root):
166 132cdb87 Michael Hanselmann
  """/2 resource.
167 132cdb87 Michael Hanselmann

168 132cdb87 Michael Hanselmann
  """
169 132cdb87 Michael Hanselmann
170 132cdb87 Michael Hanselmann
171 26ff6ee2 Michael Hanselmann
class R_version(baserlib.ResourceBase):
172 4e5a68f8 Oleksiy Mishchenko
  """/version resource.
173 4e5a68f8 Oleksiy Mishchenko

174 4e5a68f8 Oleksiy Mishchenko
  This resource should be used to determine the remote API version and
175 4e5a68f8 Oleksiy Mishchenko
  to adapt clients accordingly.
176 4e5a68f8 Oleksiy Mishchenko

177 4e5a68f8 Oleksiy Mishchenko
  """
178 7e950d31 Iustin Pop
  @staticmethod
179 7e950d31 Iustin Pop
  def GET():
180 4e5a68f8 Oleksiy Mishchenko
    """Returns the remote API version.
181 4e5a68f8 Oleksiy Mishchenko

182 4e5a68f8 Oleksiy Mishchenko
    """
183 4e5a68f8 Oleksiy Mishchenko
    return constants.RAPI_VERSION
184 4e5a68f8 Oleksiy Mishchenko
185 4e5a68f8 Oleksiy Mishchenko
186 f6ce0ba2 Michael Hanselmann
class R_2_info(baserlib.OpcodeResource):
187 b58a4d16 Michael Hanselmann
  """/2/info resource.
188 4e5a68f8 Oleksiy Mishchenko

189 4e5a68f8 Oleksiy Mishchenko
  """
190 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpClusterQuery
191 f6ce0ba2 Michael Hanselmann
192 be1ddd09 Michael Hanselmann
  def GET(self):
193 4e5a68f8 Oleksiy Mishchenko
    """Returns cluster information.
194 4e5a68f8 Oleksiy Mishchenko

195 4e5a68f8 Oleksiy Mishchenko
    """
196 be1ddd09 Michael Hanselmann
    client = self.GetClient()
197 9031ee8e Iustin Pop
    return client.QueryClusterInfo()
198 4e5a68f8 Oleksiy Mishchenko
199 4e5a68f8 Oleksiy Mishchenko
200 26ff6ee2 Michael Hanselmann
class R_2_features(baserlib.ResourceBase):
201 7eac4a4d Michael Hanselmann
  """/2/features resource.
202 7eac4a4d Michael Hanselmann

203 7eac4a4d Michael Hanselmann
  """
204 7eac4a4d Michael Hanselmann
  @staticmethod
205 7eac4a4d Michael Hanselmann
  def GET():
206 7eac4a4d Michael Hanselmann
    """Returns list of optional RAPI features implemented.
207 7eac4a4d Michael Hanselmann

208 7eac4a4d Michael Hanselmann
    """
209 b4fcee5b Michael Hanselmann
    return list(ALL_FEATURES)
210 7eac4a4d Michael Hanselmann
211 7eac4a4d Michael Hanselmann
212 f6ce0ba2 Michael Hanselmann
class R_2_os(baserlib.OpcodeResource):
213 4e5a68f8 Oleksiy Mishchenko
  """/2/os resource.
214 4e5a68f8 Oleksiy Mishchenko

215 4e5a68f8 Oleksiy Mishchenko
  """
216 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpOsDiagnose
217 f6ce0ba2 Michael Hanselmann
218 be1ddd09 Michael Hanselmann
  def GET(self):
219 4e5a68f8 Oleksiy Mishchenko
    """Return a list of all OSes.
220 4e5a68f8 Oleksiy Mishchenko

221 4e5a68f8 Oleksiy Mishchenko
    Can return error 500 in case of a problem.
222 4e5a68f8 Oleksiy Mishchenko

223 4e5a68f8 Oleksiy Mishchenko
    Example: ["debian-etch"]
224 4e5a68f8 Oleksiy Mishchenko

225 4e5a68f8 Oleksiy Mishchenko
    """
226 be1ddd09 Michael Hanselmann
    cl = self.GetClient()
227 da2d02e7 Iustin Pop
    op = opcodes.OpOsDiagnose(output_fields=["name", "variants"], names=[])
228 be1ddd09 Michael Hanselmann
    job_id = self.SubmitJob([op], cl=cl)
229 59b4eeef Iustin Pop
    # we use custom feedback function, instead of print we log the status
230 59b4eeef Iustin Pop
    result = cli.PollJob(job_id, cl, feedback_fn=baserlib.FeedbackFn)
231 59b4eeef Iustin Pop
    diagnose_data = result[0]
232 4e5a68f8 Oleksiy Mishchenko
233 4e5a68f8 Oleksiy Mishchenko
    if not isinstance(diagnose_data, list):
234 59b4eeef Iustin Pop
      raise http.HttpBadGateway(message="Can't get OS list")
235 4e5a68f8 Oleksiy Mishchenko
236 e3ac208c Guido Trotter
    os_names = []
237 d22dfef7 Iustin Pop
    for (name, variants) in diagnose_data:
238 d22dfef7 Iustin Pop
      os_names.extend(cli.CalculateOSNames(name, variants))
239 e3ac208c Guido Trotter
240 e3ac208c Guido Trotter
    return os_names
241 51ee2f49 Oleksiy Mishchenko
242 10b207d4 Oleksiy Mishchenko
243 8fd625fc Michael Hanselmann
class R_2_redist_config(baserlib.OpcodeResource):
244 508e9b20 Michael Hanselmann
  """/2/redistribute-config resource.
245 508e9b20 Michael Hanselmann

246 508e9b20 Michael Hanselmann
  """
247 8fd625fc Michael Hanselmann
  PUT_OPCODE = opcodes.OpClusterRedistConf
248 508e9b20 Michael Hanselmann
249 508e9b20 Michael Hanselmann
250 50c7d82e Michael Hanselmann
class R_2_cluster_modify(baserlib.OpcodeResource):
251 62e999a5 Michael Hanselmann
  """/2/modify resource.
252 62e999a5 Michael Hanselmann

253 62e999a5 Michael Hanselmann
  """
254 50c7d82e Michael Hanselmann
  PUT_OPCODE = opcodes.OpClusterSetParams
255 62e999a5 Michael Hanselmann
256 62e999a5 Michael Hanselmann
257 26ff6ee2 Michael Hanselmann
class R_2_jobs(baserlib.ResourceBase):
258 10b207d4 Oleksiy Mishchenko
  """/2/jobs resource.
259 10b207d4 Oleksiy Mishchenko

260 10b207d4 Oleksiy Mishchenko
  """
261 e987f166 Michael Hanselmann
  def GET(self):
262 10b207d4 Oleksiy Mishchenko
    """Returns a dictionary of jobs.
263 10b207d4 Oleksiy Mishchenko

264 c41eea6e Iustin Pop
    @return: a dictionary with jobs id and uri.
265 38206f3c Iustin Pop

266 10b207d4 Oleksiy Mishchenko
    """
267 be1ddd09 Michael Hanselmann
    client = self.GetClient()
268 e987f166 Michael Hanselmann
269 e987f166 Michael Hanselmann
    if self.useBulk():
270 bd7b2070 Michael Hanselmann
      bulkdata = client.QueryJobs(None, J_FIELDS_BULK)
271 bd7b2070 Michael Hanselmann
      return baserlib.MapBulkFields(bulkdata, J_FIELDS_BULK)
272 e987f166 Michael Hanselmann
    else:
273 e987f166 Michael Hanselmann
      jobdata = map(compat.fst, client.QueryJobs(None, ["id"]))
274 e987f166 Michael Hanselmann
      return baserlib.BuildUriList(jobdata, "/2/jobs/%s",
275 e987f166 Michael Hanselmann
                                   uri_fields=("id", "uri"))
276 10b207d4 Oleksiy Mishchenko
277 10b207d4 Oleksiy Mishchenko
278 26ff6ee2 Michael Hanselmann
class R_2_jobs_id(baserlib.ResourceBase):
279 10b207d4 Oleksiy Mishchenko
  """/2/jobs/[job_id] resource.
280 10b207d4 Oleksiy Mishchenko

281 10b207d4 Oleksiy Mishchenko
  """
282 10b207d4 Oleksiy Mishchenko
  def GET(self):
283 10b207d4 Oleksiy Mishchenko
    """Returns a job status.
284 10b207d4 Oleksiy Mishchenko

285 c41eea6e Iustin Pop
    @return: a dictionary with job parameters.
286 c41eea6e Iustin Pop
        The result includes:
287 c41eea6e Iustin Pop
            - id: job ID as a number
288 c41eea6e Iustin Pop
            - status: current job status as a string
289 c41eea6e Iustin Pop
            - ops: involved OpCodes as a list of dictionaries for each
290 c41eea6e Iustin Pop
              opcodes in the job
291 c41eea6e Iustin Pop
            - opstatus: OpCodes status as a list
292 c41eea6e Iustin Pop
            - opresult: OpCodes results as a list of lists
293 38206f3c Iustin Pop

294 10b207d4 Oleksiy Mishchenko
    """
295 10b207d4 Oleksiy Mishchenko
    job_id = self.items[0]
296 be1ddd09 Michael Hanselmann
    result = self.GetClient().QueryJobs([job_id, ], J_FIELDS)[0]
297 ee69c97f Iustin Pop
    if result is None:
298 ee69c97f Iustin Pop
      raise http.HttpNotFound()
299 e987f166 Michael Hanselmann
    return baserlib.MapFields(J_FIELDS, result)
300 10b207d4 Oleksiy Mishchenko
301 c7f5f338 Oleksiy Mishchenko
  def DELETE(self):
302 c7f5f338 Oleksiy Mishchenko
    """Cancel not-yet-started job.
303 c7f5f338 Oleksiy Mishchenko

304 c7f5f338 Oleksiy Mishchenko
    """
305 c7f5f338 Oleksiy Mishchenko
    job_id = self.items[0]
306 be1ddd09 Michael Hanselmann
    result = self.GetClient().CancelJob(job_id)
307 c7f5f338 Oleksiy Mishchenko
    return result
308 c7f5f338 Oleksiy Mishchenko
309 10b207d4 Oleksiy Mishchenko
310 26ff6ee2 Michael Hanselmann
class R_2_jobs_id_wait(baserlib.ResourceBase):
311 793a8f7c Michael Hanselmann
  """/2/jobs/[job_id]/wait resource.
312 793a8f7c Michael Hanselmann

313 793a8f7c Michael Hanselmann
  """
314 793a8f7c Michael Hanselmann
  # WaitForJobChange provides access to sensitive information and blocks
315 793a8f7c Michael Hanselmann
  # machine resources (it's a blocking RAPI call), hence restricting access.
316 793a8f7c Michael Hanselmann
  GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
317 793a8f7c Michael Hanselmann
318 793a8f7c Michael Hanselmann
  def GET(self):
319 793a8f7c Michael Hanselmann
    """Waits for job changes.
320 793a8f7c Michael Hanselmann

321 793a8f7c Michael Hanselmann
    """
322 793a8f7c Michael Hanselmann
    job_id = self.items[0]
323 793a8f7c Michael Hanselmann
324 793a8f7c Michael Hanselmann
    fields = self.getBodyParameter("fields")
325 793a8f7c Michael Hanselmann
    prev_job_info = self.getBodyParameter("previous_job_info", None)
326 793a8f7c Michael Hanselmann
    prev_log_serial = self.getBodyParameter("previous_log_serial", None)
327 793a8f7c Michael Hanselmann
328 793a8f7c Michael Hanselmann
    if not isinstance(fields, list):
329 793a8f7c Michael Hanselmann
      raise http.HttpBadRequest("The 'fields' parameter should be a list")
330 793a8f7c Michael Hanselmann
331 793a8f7c Michael Hanselmann
    if not (prev_job_info is None or isinstance(prev_job_info, list)):
332 793a8f7c Michael Hanselmann
      raise http.HttpBadRequest("The 'previous_job_info' parameter should"
333 793a8f7c Michael Hanselmann
                                " be a list")
334 793a8f7c Michael Hanselmann
335 793a8f7c Michael Hanselmann
    if not (prev_log_serial is None or
336 793a8f7c Michael Hanselmann
            isinstance(prev_log_serial, (int, long))):
337 793a8f7c Michael Hanselmann
      raise http.HttpBadRequest("The 'previous_log_serial' parameter should"
338 793a8f7c Michael Hanselmann
                                " be a number")
339 793a8f7c Michael Hanselmann
340 be1ddd09 Michael Hanselmann
    client = self.GetClient()
341 793a8f7c Michael Hanselmann
    result = client.WaitForJobChangeOnce(job_id, fields,
342 793a8f7c Michael Hanselmann
                                         prev_job_info, prev_log_serial,
343 793a8f7c Michael Hanselmann
                                         timeout=_WFJC_TIMEOUT)
344 793a8f7c Michael Hanselmann
    if not result:
345 793a8f7c Michael Hanselmann
      raise http.HttpNotFound()
346 793a8f7c Michael Hanselmann
347 793a8f7c Michael Hanselmann
    if result == constants.JOB_NOTCHANGED:
348 793a8f7c Michael Hanselmann
      # No changes
349 793a8f7c Michael Hanselmann
      return None
350 793a8f7c Michael Hanselmann
351 793a8f7c Michael Hanselmann
    (job_info, log_entries) = result
352 793a8f7c Michael Hanselmann
353 793a8f7c Michael Hanselmann
    return {
354 793a8f7c Michael Hanselmann
      "job_info": job_info,
355 793a8f7c Michael Hanselmann
      "log_entries": log_entries,
356 793a8f7c Michael Hanselmann
      }
357 793a8f7c Michael Hanselmann
358 793a8f7c Michael Hanselmann
359 f6ce0ba2 Michael Hanselmann
class R_2_nodes(baserlib.OpcodeResource):
360 10b207d4 Oleksiy Mishchenko
  """/2/nodes resource.
361 10b207d4 Oleksiy Mishchenko

362 10b207d4 Oleksiy Mishchenko
  """
363 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpNodeQuery
364 f6ce0ba2 Michael Hanselmann
365 10b207d4 Oleksiy Mishchenko
  def GET(self):
366 10b207d4 Oleksiy Mishchenko
    """Returns a list of all nodes.
367 38206f3c Iustin Pop

368 10b207d4 Oleksiy Mishchenko
    """
369 be1ddd09 Michael Hanselmann
    client = self.GetClient()
370 38206f3c Iustin Pop
371 3d103742 Iustin Pop
    if self.useBulk():
372 9031ee8e Iustin Pop
      bulkdata = client.QueryNodes([], N_FIELDS, False)
373 a0dcf7c2 Oleksiy Mishchenko
      return baserlib.MapBulkFields(bulkdata, N_FIELDS)
374 9031ee8e Iustin Pop
    else:
375 9031ee8e Iustin Pop
      nodesdata = client.QueryNodes([], ["name"], False)
376 9031ee8e Iustin Pop
      nodeslist = [row[0] for row in nodesdata]
377 9031ee8e Iustin Pop
      return baserlib.BuildUriList(nodeslist, "/2/nodes/%s",
378 9031ee8e Iustin Pop
                                   uri_fields=("id", "uri"))
379 441e7cfd Oleksiy Mishchenko
380 441e7cfd Oleksiy Mishchenko
381 f6ce0ba2 Michael Hanselmann
class R_2_nodes_name(baserlib.OpcodeResource):
382 b58a4d16 Michael Hanselmann
  """/2/nodes/[node_name] resource.
383 028c6b76 Oleksiy Mishchenko

384 028c6b76 Oleksiy Mishchenko
  """
385 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpNodeQuery
386 f6ce0ba2 Michael Hanselmann
387 4e5a68f8 Oleksiy Mishchenko
  def GET(self):
388 4e5a68f8 Oleksiy Mishchenko
    """Send information about a node.
389 4e5a68f8 Oleksiy Mishchenko

390 4e5a68f8 Oleksiy Mishchenko
    """
391 4e5a68f8 Oleksiy Mishchenko
    node_name = self.items[0]
392 be1ddd09 Michael Hanselmann
    client = self.GetClient()
393 e8ebbd2b Michael Hanselmann
394 e8ebbd2b Michael Hanselmann
    result = baserlib.HandleItemQueryErrors(client.QueryNodes,
395 e8ebbd2b Michael Hanselmann
                                            names=[node_name], fields=N_FIELDS,
396 e8ebbd2b Michael Hanselmann
                                            use_locking=self.useLocking())
397 4e5a68f8 Oleksiy Mishchenko
398 4e5a68f8 Oleksiy Mishchenko
    return baserlib.MapFields(N_FIELDS, result[0])
399 028c6b76 Oleksiy Mishchenko
400 028c6b76 Oleksiy Mishchenko
401 42d4d8b9 Michael Hanselmann
class R_2_nodes_name_powercycle(baserlib.OpcodeResource):
402 42d4d8b9 Michael Hanselmann
  """/2/nodes/[node_name]/powercycle resource.
403 42d4d8b9 Michael Hanselmann

404 42d4d8b9 Michael Hanselmann
  """
405 42d4d8b9 Michael Hanselmann
  POST_OPCODE = opcodes.OpNodePowercycle
406 42d4d8b9 Michael Hanselmann
407 42d4d8b9 Michael Hanselmann
  def GetPostOpInput(self):
408 42d4d8b9 Michael Hanselmann
    """Tries to powercycle a node.
409 42d4d8b9 Michael Hanselmann

410 42d4d8b9 Michael Hanselmann
    """
411 42d4d8b9 Michael Hanselmann
    return (self.request_body, {
412 42d4d8b9 Michael Hanselmann
      "node_name": self.items[0],
413 42d4d8b9 Michael Hanselmann
      "force": self.useForce(),
414 42d4d8b9 Michael Hanselmann
      })
415 42d4d8b9 Michael Hanselmann
416 42d4d8b9 Michael Hanselmann
417 51cc8637 Michael Hanselmann
class R_2_nodes_name_role(baserlib.OpcodeResource):
418 51cc8637 Michael Hanselmann
  """/2/nodes/[node_name]/role resource.
419 64dae8fc Michael Hanselmann

420 64dae8fc Michael Hanselmann
  """
421 51cc8637 Michael Hanselmann
  PUT_OPCODE = opcodes.OpNodeSetParams
422 51cc8637 Michael Hanselmann
423 64dae8fc Michael Hanselmann
  def GET(self):
424 64dae8fc Michael Hanselmann
    """Returns the current node role.
425 64dae8fc Michael Hanselmann

426 64dae8fc Michael Hanselmann
    @return: Node role
427 64dae8fc Michael Hanselmann

428 64dae8fc Michael Hanselmann
    """
429 64dae8fc Michael Hanselmann
    node_name = self.items[0]
430 be1ddd09 Michael Hanselmann
    client = self.GetClient()
431 64dae8fc Michael Hanselmann
    result = client.QueryNodes(names=[node_name], fields=["role"],
432 64dae8fc Michael Hanselmann
                               use_locking=self.useLocking())
433 64dae8fc Michael Hanselmann
434 64dae8fc Michael Hanselmann
    return _NR_MAP[result[0][0]]
435 64dae8fc Michael Hanselmann
436 51cc8637 Michael Hanselmann
  def GetPutOpInput(self):
437 64dae8fc Michael Hanselmann
    """Sets the node role.
438 64dae8fc Michael Hanselmann

439 64dae8fc Michael Hanselmann
    """
440 51cc8637 Michael Hanselmann
    baserlib.CheckType(self.request_body, basestring, "Body contents")
441 64dae8fc Michael Hanselmann
442 627ad739 Michael Hanselmann
    role = self.request_body
443 64dae8fc Michael Hanselmann
444 64dae8fc Michael Hanselmann
    if role == _NR_REGULAR:
445 64dae8fc Michael Hanselmann
      candidate = False
446 64dae8fc Michael Hanselmann
      offline = False
447 64dae8fc Michael Hanselmann
      drained = False
448 64dae8fc Michael Hanselmann
449 51cc8637 Michael Hanselmann
    elif role == _NR_MASTER_CANDIDATE:
450 64dae8fc Michael Hanselmann
      candidate = True
451 64dae8fc Michael Hanselmann
      offline = drained = None
452 64dae8fc Michael Hanselmann
453 64dae8fc Michael Hanselmann
    elif role == _NR_DRAINED:
454 64dae8fc Michael Hanselmann
      drained = True
455 64dae8fc Michael Hanselmann
      candidate = offline = None
456 64dae8fc Michael Hanselmann
457 64dae8fc Michael Hanselmann
    elif role == _NR_OFFLINE:
458 64dae8fc Michael Hanselmann
      offline = True
459 64dae8fc Michael Hanselmann
      candidate = drained = None
460 64dae8fc Michael Hanselmann
461 64dae8fc Michael Hanselmann
    else:
462 64dae8fc Michael Hanselmann
      raise http.HttpBadRequest("Can't set '%s' role" % role)
463 64dae8fc Michael Hanselmann
464 51cc8637 Michael Hanselmann
    assert len(self.items) == 1
465 64dae8fc Michael Hanselmann
466 51cc8637 Michael Hanselmann
    return ({}, {
467 51cc8637 Michael Hanselmann
      "node_name": self.items[0],
468 51cc8637 Michael Hanselmann
      "master_candidate": candidate,
469 51cc8637 Michael Hanselmann
      "offline": offline,
470 51cc8637 Michael Hanselmann
      "drained": drained,
471 51cc8637 Michael Hanselmann
      "force": self.useForce(),
472 682878d9 Guido Trotter
      "auto_promote": bool(self._checkIntVariable("auto-promote", default=0)),
473 51cc8637 Michael Hanselmann
      })
474 64dae8fc Michael Hanselmann
475 64dae8fc Michael Hanselmann
476 7fa91722 Michael Hanselmann
class R_2_nodes_name_evacuate(baserlib.OpcodeResource):
477 73452f12 Michael Hanselmann
  """/2/nodes/[node_name]/evacuate resource.
478 73452f12 Michael Hanselmann

479 73452f12 Michael Hanselmann
  """
480 7fa91722 Michael Hanselmann
  POST_OPCODE = opcodes.OpNodeEvacuate
481 7fa91722 Michael Hanselmann
482 7fa91722 Michael Hanselmann
  def GetPostOpInput(self):
483 de40437a Michael Hanselmann
    """Evacuate all instances off a node.
484 73452f12 Michael Hanselmann

485 73452f12 Michael Hanselmann
    """
486 7fa91722 Michael Hanselmann
    return (self.request_body, {
487 de40437a Michael Hanselmann
      "node_name": self.items[0],
488 de40437a Michael Hanselmann
      "dry_run": self.dryRun(),
489 de40437a Michael Hanselmann
      })
490 941b9309 Iustin Pop
491 73452f12 Michael Hanselmann
492 55168cc7 Michael Hanselmann
class R_2_nodes_name_migrate(baserlib.OpcodeResource):
493 7a95a954 Michael Hanselmann
  """/2/nodes/[node_name]/migrate resource.
494 1c482bab Michael Hanselmann

495 1c482bab Michael Hanselmann
  """
496 55168cc7 Michael Hanselmann
  POST_OPCODE = opcodes.OpNodeMigrate
497 55168cc7 Michael Hanselmann
498 55168cc7 Michael Hanselmann
  def GetPostOpInput(self):
499 1c482bab Michael Hanselmann
    """Migrate all primary instances from a node.
500 1c482bab Michael Hanselmann

501 1c482bab Michael Hanselmann
    """
502 b7a1c816 Michael Hanselmann
    if self.queryargs:
503 b7a1c816 Michael Hanselmann
      # Support old-style requests
504 b7a1c816 Michael Hanselmann
      if "live" in self.queryargs and "mode" in self.queryargs:
505 b7a1c816 Michael Hanselmann
        raise http.HttpBadRequest("Only one of 'live' and 'mode' should"
506 b7a1c816 Michael Hanselmann
                                  " be passed")
507 b7a1c816 Michael Hanselmann
508 b7a1c816 Michael Hanselmann
      if "live" in self.queryargs:
509 b7a1c816 Michael Hanselmann
        if self._checkIntVariable("live", default=1):
510 b7a1c816 Michael Hanselmann
          mode = constants.HT_MIGRATION_LIVE
511 b7a1c816 Michael Hanselmann
        else:
512 b7a1c816 Michael Hanselmann
          mode = constants.HT_MIGRATION_NONLIVE
513 52194140 Iustin Pop
      else:
514 b7a1c816 Michael Hanselmann
        mode = self._checkStringVariable("mode", default=None)
515 b7a1c816 Michael Hanselmann
516 b7a1c816 Michael Hanselmann
      data = {
517 b7a1c816 Michael Hanselmann
        "mode": mode,
518 b7a1c816 Michael Hanselmann
        }
519 52194140 Iustin Pop
    else:
520 b7a1c816 Michael Hanselmann
      data = self.request_body
521 52194140 Iustin Pop
522 55168cc7 Michael Hanselmann
    return (data, {
523 55168cc7 Michael Hanselmann
      "node_name": self.items[0],
524 b7a1c816 Michael Hanselmann
      })
525 1c482bab Michael Hanselmann
526 1c482bab Michael Hanselmann
527 682878d9 Guido Trotter
class R_2_nodes_name_modify(baserlib.OpcodeResource):
528 94497dd1 Michael Hanselmann
  """/2/nodes/[node_name]/modify resource.
529 94497dd1 Michael Hanselmann

530 94497dd1 Michael Hanselmann
  """
531 8b8f54dd Guido Trotter
  POST_OPCODE = opcodes.OpNodeSetParams
532 94497dd1 Michael Hanselmann
533 8b8f54dd Guido Trotter
  def GetPostOpInput(self):
534 682878d9 Guido Trotter
    """Changes parameters of a node.
535 94497dd1 Michael Hanselmann

536 94497dd1 Michael Hanselmann
    """
537 682878d9 Guido Trotter
    assert len(self.items) == 1
538 94497dd1 Michael Hanselmann
539 682878d9 Guido Trotter
    return (self.request_body, {
540 370f2042 Guido Trotter
      "node_name": self.items[0],
541 94497dd1 Michael Hanselmann
      })
542 94497dd1 Michael Hanselmann
543 94497dd1 Michael Hanselmann
544 eb08e09d Michael Hanselmann
class R_2_nodes_name_storage(baserlib.OpcodeResource):
545 b58a4d16 Michael Hanselmann
  """/2/nodes/[node_name]/storage resource.
546 7a95a954 Michael Hanselmann

547 7a95a954 Michael Hanselmann
  """
548 ad8d0595 Iustin Pop
  # LUNodeQueryStorage acquires locks, hence restricting access to GET
549 7a95a954 Michael Hanselmann
  GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
550 eb08e09d Michael Hanselmann
  GET_OPCODE = opcodes.OpNodeQueryStorage
551 7a95a954 Michael Hanselmann
552 eb08e09d Michael Hanselmann
  def GetGetOpInput(self):
553 eb08e09d Michael Hanselmann
    """List storage available on a node.
554 7a95a954 Michael Hanselmann

555 eb08e09d Michael Hanselmann
    """
556 7a95a954 Michael Hanselmann
    storage_type = self._checkStringVariable("storage_type", None)
557 7a95a954 Michael Hanselmann
    output_fields = self._checkStringVariable("output_fields", None)
558 eb08e09d Michael Hanselmann
559 7a95a954 Michael Hanselmann
    if not output_fields:
560 7a95a954 Michael Hanselmann
      raise http.HttpBadRequest("Missing the required 'output_fields'"
561 7a95a954 Michael Hanselmann
                                " parameter")
562 7a95a954 Michael Hanselmann
563 eb08e09d Michael Hanselmann
    return ({}, {
564 eb08e09d Michael Hanselmann
      "nodes": [self.items[0]],
565 eb08e09d Michael Hanselmann
      "storage_type": storage_type,
566 eb08e09d Michael Hanselmann
      "output_fields": output_fields.split(","),
567 eb08e09d Michael Hanselmann
      })
568 7a95a954 Michael Hanselmann
569 7a95a954 Michael Hanselmann
570 8d232068 Michael Hanselmann
class R_2_nodes_name_storage_modify(baserlib.OpcodeResource):
571 b58a4d16 Michael Hanselmann
  """/2/nodes/[node_name]/storage/modify resource.
572 1e82bc80 Michael Hanselmann

573 1e82bc80 Michael Hanselmann
  """
574 8d232068 Michael Hanselmann
  PUT_OPCODE = opcodes.OpNodeModifyStorage
575 1e82bc80 Michael Hanselmann
576 8d232068 Michael Hanselmann
  def GetPutOpInput(self):
577 8d232068 Michael Hanselmann
    """Modifies a storage volume on a node.
578 1e82bc80 Michael Hanselmann

579 8d232068 Michael Hanselmann
    """
580 8d232068 Michael Hanselmann
    storage_type = self._checkStringVariable("storage_type", None)
581 1e82bc80 Michael Hanselmann
    name = self._checkStringVariable("name", None)
582 8d232068 Michael Hanselmann
583 1e82bc80 Michael Hanselmann
    if not name:
584 1e82bc80 Michael Hanselmann
      raise http.HttpBadRequest("Missing the required 'name'"
585 1e82bc80 Michael Hanselmann
                                " parameter")
586 1e82bc80 Michael Hanselmann
587 1e82bc80 Michael Hanselmann
    changes = {}
588 1e82bc80 Michael Hanselmann
589 1e82bc80 Michael Hanselmann
    if "allocatable" in self.queryargs:
590 1e82bc80 Michael Hanselmann
      changes[constants.SF_ALLOCATABLE] = \
591 1e82bc80 Michael Hanselmann
        bool(self._checkIntVariable("allocatable", default=1))
592 1e82bc80 Michael Hanselmann
593 8d232068 Michael Hanselmann
    return ({}, {
594 8d232068 Michael Hanselmann
      "node_name": self.items[0],
595 8d232068 Michael Hanselmann
      "storage_type": storage_type,
596 8d232068 Michael Hanselmann
      "name": name,
597 8d232068 Michael Hanselmann
      "changes": changes,
598 8d232068 Michael Hanselmann
      })
599 1e82bc80 Michael Hanselmann
600 1e82bc80 Michael Hanselmann
601 98270691 Michael Hanselmann
class R_2_nodes_name_storage_repair(baserlib.OpcodeResource):
602 b58a4d16 Michael Hanselmann
  """/2/nodes/[node_name]/storage/repair resource.
603 723f4565 Michael Hanselmann

604 723f4565 Michael Hanselmann
  """
605 98270691 Michael Hanselmann
  PUT_OPCODE = opcodes.OpRepairNodeStorage
606 723f4565 Michael Hanselmann
607 98270691 Michael Hanselmann
  def GetPutOpInput(self):
608 98270691 Michael Hanselmann
    """Repairs a storage volume on a node.
609 723f4565 Michael Hanselmann

610 98270691 Michael Hanselmann
    """
611 98270691 Michael Hanselmann
    storage_type = self._checkStringVariable("storage_type", None)
612 723f4565 Michael Hanselmann
    name = self._checkStringVariable("name", None)
613 723f4565 Michael Hanselmann
    if not name:
614 723f4565 Michael Hanselmann
      raise http.HttpBadRequest("Missing the required 'name'"
615 723f4565 Michael Hanselmann
                                " parameter")
616 723f4565 Michael Hanselmann
617 98270691 Michael Hanselmann
    return ({}, {
618 98270691 Michael Hanselmann
      "node_name": self.items[0],
619 98270691 Michael Hanselmann
      "storage_type": storage_type,
620 98270691 Michael Hanselmann
      "name": name,
621 98270691 Michael Hanselmann
      })
622 723f4565 Michael Hanselmann
623 723f4565 Michael Hanselmann
624 c91407bc Michael Hanselmann
class R_2_groups(baserlib.OpcodeResource):
625 c91407bc Michael Hanselmann
  """/2/groups resource.
626 90e99856 Adeodato Simo

627 90e99856 Adeodato Simo
  """
628 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpGroupQuery
629 c91407bc Michael Hanselmann
  POST_OPCODE = opcodes.OpGroupAdd
630 c91407bc Michael Hanselmann
  POST_RENAME = {
631 527fbde8 Michael Hanselmann
    "name": "group_name",
632 527fbde8 Michael Hanselmann
    }
633 527fbde8 Michael Hanselmann
634 c91407bc Michael Hanselmann
  def GetPostOpInput(self):
635 c91407bc Michael Hanselmann
    """Create a node group.
636 90e99856 Adeodato Simo

637 c91407bc Michael Hanselmann
    """
638 c91407bc Michael Hanselmann
    assert not self.items
639 c91407bc Michael Hanselmann
    return (self.request_body, {
640 c91407bc Michael Hanselmann
      "dry_run": self.dryRun(),
641 c91407bc Michael Hanselmann
      })
642 0897dc97 Adeodato Simo
643 0897dc97 Adeodato Simo
  def GET(self):
644 0897dc97 Adeodato Simo
    """Returns a list of all node groups.
645 0897dc97 Adeodato Simo

646 0897dc97 Adeodato Simo
    """
647 be1ddd09 Michael Hanselmann
    client = self.GetClient()
648 0897dc97 Adeodato Simo
649 0897dc97 Adeodato Simo
    if self.useBulk():
650 0897dc97 Adeodato Simo
      bulkdata = client.QueryGroups([], G_FIELDS, False)
651 0897dc97 Adeodato Simo
      return baserlib.MapBulkFields(bulkdata, G_FIELDS)
652 0897dc97 Adeodato Simo
    else:
653 0897dc97 Adeodato Simo
      data = client.QueryGroups([], ["name"], False)
654 0897dc97 Adeodato Simo
      groupnames = [row[0] for row in data]
655 0897dc97 Adeodato Simo
      return baserlib.BuildUriList(groupnames, "/2/groups/%s",
656 0897dc97 Adeodato Simo
                                   uri_fields=("name", "uri"))
657 0897dc97 Adeodato Simo
658 0897dc97 Adeodato Simo
659 86f1f5d4 Michael Hanselmann
class R_2_groups_name(baserlib.OpcodeResource):
660 b58a4d16 Michael Hanselmann
  """/2/groups/[group_name] resource.
661 0897dc97 Adeodato Simo

662 0897dc97 Adeodato Simo
  """
663 86f1f5d4 Michael Hanselmann
  DELETE_OPCODE = opcodes.OpGroupRemove
664 86f1f5d4 Michael Hanselmann
665 0897dc97 Adeodato Simo
  def GET(self):
666 0897dc97 Adeodato Simo
    """Send information about a node group.
667 0897dc97 Adeodato Simo

668 0897dc97 Adeodato Simo
    """
669 0897dc97 Adeodato Simo
    group_name = self.items[0]
670 be1ddd09 Michael Hanselmann
    client = self.GetClient()
671 0897dc97 Adeodato Simo
672 0897dc97 Adeodato Simo
    result = baserlib.HandleItemQueryErrors(client.QueryGroups,
673 0897dc97 Adeodato Simo
                                            names=[group_name], fields=G_FIELDS,
674 0897dc97 Adeodato Simo
                                            use_locking=self.useLocking())
675 0897dc97 Adeodato Simo
676 0897dc97 Adeodato Simo
    return baserlib.MapFields(G_FIELDS, result[0])
677 0897dc97 Adeodato Simo
678 86f1f5d4 Michael Hanselmann
  def GetDeleteOpInput(self):
679 0dbaa9ca Adeodato Simo
    """Delete a node group.
680 0dbaa9ca Adeodato Simo

681 0dbaa9ca Adeodato Simo
    """
682 86f1f5d4 Michael Hanselmann
    assert len(self.items) == 1
683 86f1f5d4 Michael Hanselmann
    return ({}, {
684 86f1f5d4 Michael Hanselmann
      "group_name": self.items[0],
685 86f1f5d4 Michael Hanselmann
      "dry_run": self.dryRun(),
686 86f1f5d4 Michael Hanselmann
      })
687 0dbaa9ca Adeodato Simo
688 0dbaa9ca Adeodato Simo
689 d5211458 Michael Hanselmann
class R_2_groups_name_modify(baserlib.OpcodeResource):
690 f18fab7d Adeodato Simo
  """/2/groups/[group_name]/modify resource.
691 f18fab7d Adeodato Simo

692 f18fab7d Adeodato Simo
  """
693 d5211458 Michael Hanselmann
  PUT_OPCODE = opcodes.OpGroupSetParams
694 f18fab7d Adeodato Simo
695 d5211458 Michael Hanselmann
  def GetPutOpInput(self):
696 d5211458 Michael Hanselmann
    """Changes some parameters of node group.
697 f18fab7d Adeodato Simo

698 f18fab7d Adeodato Simo
    """
699 d5211458 Michael Hanselmann
    assert self.items
700 d5211458 Michael Hanselmann
    return (self.request_body, {
701 d5211458 Michael Hanselmann
      "group_name": self.items[0],
702 d5211458 Michael Hanselmann
      })
703 f18fab7d Adeodato Simo
704 f18fab7d Adeodato Simo
705 cd0d4d5a Michael Hanselmann
class R_2_groups_name_rename(baserlib.OpcodeResource):
706 b58a4d16 Michael Hanselmann
  """/2/groups/[group_name]/rename resource.
707 0dbaa9ca Adeodato Simo

708 0dbaa9ca Adeodato Simo
  """
709 cd0d4d5a Michael Hanselmann
  PUT_OPCODE = opcodes.OpGroupRename
710 0dbaa9ca Adeodato Simo
711 cd0d4d5a Michael Hanselmann
  def GetPutOpInput(self):
712 cd0d4d5a Michael Hanselmann
    """Changes the name of a node group.
713 0dbaa9ca Adeodato Simo

714 0dbaa9ca Adeodato Simo
    """
715 cd0d4d5a Michael Hanselmann
    assert len(self.items) == 1
716 cd0d4d5a Michael Hanselmann
    return (self.request_body, {
717 cd0d4d5a Michael Hanselmann
      "group_name": self.items[0],
718 cd0d4d5a Michael Hanselmann
      "dry_run": self.dryRun(),
719 cd0d4d5a Michael Hanselmann
      })
720 0897dc97 Adeodato Simo
721 0897dc97 Adeodato Simo
722 87fd3ec7 Michael Hanselmann
class R_2_groups_name_assign_nodes(baserlib.OpcodeResource):
723 b58a4d16 Michael Hanselmann
  """/2/groups/[group_name]/assign-nodes resource.
724 4245446f Adeodato Simo

725 4245446f Adeodato Simo
  """
726 87fd3ec7 Michael Hanselmann
  PUT_OPCODE = opcodes.OpGroupAssignNodes
727 4245446f Adeodato Simo
728 87fd3ec7 Michael Hanselmann
  def GetPutOpInput(self):
729 87fd3ec7 Michael Hanselmann
    """Assigns nodes to a group.
730 4245446f Adeodato Simo

731 4245446f Adeodato Simo
    """
732 87fd3ec7 Michael Hanselmann
    assert len(self.items) == 1
733 87fd3ec7 Michael Hanselmann
    return (self.request_body, {
734 4245446f Adeodato Simo
      "group_name": self.items[0],
735 4245446f Adeodato Simo
      "dry_run": self.dryRun(),
736 4245446f Adeodato Simo
      "force": self.useForce(),
737 4245446f Adeodato Simo
      })
738 4245446f Adeodato Simo
739 4245446f Adeodato Simo
740 09a43b39 Michael Hanselmann
class R_2_instances(baserlib.OpcodeResource):
741 09a43b39 Michael Hanselmann
  """/2/instances resource.
742 6395cebb Michael Hanselmann

743 6395cebb Michael Hanselmann
  """
744 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpInstanceQuery
745 09a43b39 Michael Hanselmann
  POST_OPCODE = opcodes.OpInstanceCreate
746 09a43b39 Michael Hanselmann
  POST_RENAME = {
747 526a662a Michael Hanselmann
    "os": "os_type",
748 526a662a Michael Hanselmann
    "name": "instance_name",
749 526a662a Michael Hanselmann
    }
750 526a662a Michael Hanselmann
751 441e7cfd Oleksiy Mishchenko
  def GET(self):
752 441e7cfd Oleksiy Mishchenko
    """Returns a list of all available instances.
753 441e7cfd Oleksiy Mishchenko

754 441e7cfd Oleksiy Mishchenko
    """
755 be1ddd09 Michael Hanselmann
    client = self.GetClient()
756 441e7cfd Oleksiy Mishchenko
757 3d103742 Iustin Pop
    use_locking = self.useLocking()
758 3d103742 Iustin Pop
    if self.useBulk():
759 3d103742 Iustin Pop
      bulkdata = client.QueryInstances([], I_FIELDS, use_locking)
760 a0dcf7c2 Oleksiy Mishchenko
      return baserlib.MapBulkFields(bulkdata, I_FIELDS)
761 441e7cfd Oleksiy Mishchenko
    else:
762 3d103742 Iustin Pop
      instancesdata = client.QueryInstances([], ["name"], use_locking)
763 9031ee8e Iustin Pop
      instanceslist = [row[0] for row in instancesdata]
764 441e7cfd Oleksiy Mishchenko
      return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
765 441e7cfd Oleksiy Mishchenko
                                   uri_fields=("id", "uri"))
766 441e7cfd Oleksiy Mishchenko
767 09a43b39 Michael Hanselmann
  def GetPostOpInput(self):
768 d975f482 Michael Hanselmann
    """Create an instance.
769 d975f482 Michael Hanselmann

770 d975f482 Michael Hanselmann
    @return: a job id
771 d975f482 Michael Hanselmann

772 d975f482 Michael Hanselmann
    """
773 09a43b39 Michael Hanselmann
    baserlib.CheckType(self.request_body, dict, "Body contents")
774 d975f482 Michael Hanselmann
775 d975f482 Michael Hanselmann
    # Default to request data version 0
776 d975f482 Michael Hanselmann
    data_version = self.getBodyParameter(_REQ_DATA_VERSION, 0)
777 d975f482 Michael Hanselmann
778 d975f482 Michael Hanselmann
    if data_version == 0:
779 3fd7f652 Michael Hanselmann
      raise http.HttpBadRequest("Instance creation request version 0 is no"
780 3fd7f652 Michael Hanselmann
                                " longer supported")
781 09a43b39 Michael Hanselmann
    elif data_version != 1:
782 d975f482 Michael Hanselmann
      raise http.HttpBadRequest("Unsupported request data version %s" %
783 12eff9b9 Michael Hanselmann
                                data_version)
784 d975f482 Michael Hanselmann
785 09a43b39 Michael Hanselmann
    data = self.request_body.copy()
786 09a43b39 Michael Hanselmann
    # Remove "__version__"
787 09a43b39 Michael Hanselmann
    data.pop(_REQ_DATA_VERSION, None)
788 09a43b39 Michael Hanselmann
789 09a43b39 Michael Hanselmann
    return (data, {
790 09a43b39 Michael Hanselmann
      "dry_run": self.dryRun(),
791 09a43b39 Michael Hanselmann
      })
792 2f7635f4 Oleksiy Mishchenko
793 441e7cfd Oleksiy Mishchenko
794 a6fa7892 Michael Hanselmann
class R_2_instances_name(baserlib.OpcodeResource):
795 b58a4d16 Michael Hanselmann
  """/2/instances/[instance_name] resource.
796 028c6b76 Oleksiy Mishchenko

797 028c6b76 Oleksiy Mishchenko
  """
798 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpInstanceQuery
799 a6fa7892 Michael Hanselmann
  DELETE_OPCODE = opcodes.OpInstanceRemove
800 a6fa7892 Michael Hanselmann
801 4e5a68f8 Oleksiy Mishchenko
  def GET(self):
802 4e5a68f8 Oleksiy Mishchenko
    """Send information about an instance.
803 4e5a68f8 Oleksiy Mishchenko

804 4e5a68f8 Oleksiy Mishchenko
    """
805 be1ddd09 Michael Hanselmann
    client = self.GetClient()
806 4e5a68f8 Oleksiy Mishchenko
    instance_name = self.items[0]
807 e8ebbd2b Michael Hanselmann
808 e8ebbd2b Michael Hanselmann
    result = baserlib.HandleItemQueryErrors(client.QueryInstances,
809 e8ebbd2b Michael Hanselmann
                                            names=[instance_name],
810 e8ebbd2b Michael Hanselmann
                                            fields=I_FIELDS,
811 e8ebbd2b Michael Hanselmann
                                            use_locking=self.useLocking())
812 4e5a68f8 Oleksiy Mishchenko
813 4e5a68f8 Oleksiy Mishchenko
    return baserlib.MapFields(I_FIELDS, result[0])
814 028c6b76 Oleksiy Mishchenko
815 a6fa7892 Michael Hanselmann
  def GetDeleteOpInput(self):
816 6e99c5a0 Iustin Pop
    """Delete an instance.
817 6e99c5a0 Iustin Pop

818 6e99c5a0 Iustin Pop
    """
819 a6fa7892 Michael Hanselmann
    assert len(self.items) == 1
820 a6fa7892 Michael Hanselmann
    return ({}, {
821 a6fa7892 Michael Hanselmann
      "instance_name": self.items[0],
822 a6fa7892 Michael Hanselmann
      "ignore_failures": False,
823 a6fa7892 Michael Hanselmann
      "dry_run": self.dryRun(),
824 a6fa7892 Michael Hanselmann
      })
825 6e99c5a0 Iustin Pop
826 028c6b76 Oleksiy Mishchenko
827 7b3df961 Michael Hanselmann
class R_2_instances_name_info(baserlib.OpcodeResource):
828 d8260842 Michael Hanselmann
  """/2/instances/[instance_name]/info resource.
829 d8260842 Michael Hanselmann

830 d8260842 Michael Hanselmann
  """
831 7b3df961 Michael Hanselmann
  GET_OPCODE = opcodes.OpInstanceQueryData
832 7b3df961 Michael Hanselmann
833 7b3df961 Michael Hanselmann
  def GetGetOpInput(self):
834 d8260842 Michael Hanselmann
    """Request detailed instance information.
835 d8260842 Michael Hanselmann

836 d8260842 Michael Hanselmann
    """
837 7b3df961 Michael Hanselmann
    assert len(self.items) == 1
838 7b3df961 Michael Hanselmann
    return ({}, {
839 7b3df961 Michael Hanselmann
      "instances": [self.items[0]],
840 7b3df961 Michael Hanselmann
      "static": bool(self._checkIntVariable("static", default=0)),
841 7b3df961 Michael Hanselmann
      })
842 d8260842 Michael Hanselmann
843 d8260842 Michael Hanselmann
844 ac4888c1 Michael Hanselmann
class R_2_instances_name_reboot(baserlib.OpcodeResource):
845 2276aa29 Oleksiy Mishchenko
  """/2/instances/[instance_name]/reboot resource.
846 2276aa29 Oleksiy Mishchenko

847 2276aa29 Oleksiy Mishchenko
  Implements an instance reboot.
848 2276aa29 Oleksiy Mishchenko

849 2276aa29 Oleksiy Mishchenko
  """
850 ac4888c1 Michael Hanselmann
  POST_OPCODE = opcodes.OpInstanceReboot
851 ac4888c1 Michael Hanselmann
852 ac4888c1 Michael Hanselmann
  def GetPostOpInput(self):
853 2276aa29 Oleksiy Mishchenko
    """Reboot an instance.
854 2276aa29 Oleksiy Mishchenko

855 0c55c24b Oleksiy Mishchenko
    The URI takes type=[hard|soft|full] and
856 0c55c24b Oleksiy Mishchenko
    ignore_secondaries=[False|True] parameters.
857 0c55c24b Oleksiy Mishchenko

858 2276aa29 Oleksiy Mishchenko
    """
859 ac4888c1 Michael Hanselmann
    return ({}, {
860 ac4888c1 Michael Hanselmann
      "instance_name": self.items[0],
861 ac4888c1 Michael Hanselmann
      "reboot_type":
862 ac4888c1 Michael Hanselmann
        self.queryargs.get("type", [constants.INSTANCE_REBOOT_HARD])[0],
863 ac4888c1 Michael Hanselmann
      "ignore_secondaries": bool(self._checkIntVariable("ignore_secondaries")),
864 ac4888c1 Michael Hanselmann
      "dry_run": self.dryRun(),
865 ac4888c1 Michael Hanselmann
      })
866 2276aa29 Oleksiy Mishchenko
867 2276aa29 Oleksiy Mishchenko
868 4717e6eb Michael Hanselmann
class R_2_instances_name_startup(baserlib.OpcodeResource):
869 0c55c24b Oleksiy Mishchenko
  """/2/instances/[instance_name]/startup resource.
870 0c55c24b Oleksiy Mishchenko

871 0c55c24b Oleksiy Mishchenko
  Implements an instance startup.
872 0c55c24b Oleksiy Mishchenko

873 0c55c24b Oleksiy Mishchenko
  """
874 4717e6eb Michael Hanselmann
  PUT_OPCODE = opcodes.OpInstanceStartup
875 4717e6eb Michael Hanselmann
876 4717e6eb Michael Hanselmann
  def GetPutOpInput(self):
877 0c55c24b Oleksiy Mishchenko
    """Startup an instance.
878 0c55c24b Oleksiy Mishchenko

879 c41eea6e Iustin Pop
    The URI takes force=[False|True] parameter to start the instance
880 c41eea6e Iustin Pop
    if even if secondary disks are failing.
881 0c55c24b Oleksiy Mishchenko

882 0c55c24b Oleksiy Mishchenko
    """
883 4717e6eb Michael Hanselmann
    return ({}, {
884 4717e6eb Michael Hanselmann
      "instance_name": self.items[0],
885 4717e6eb Michael Hanselmann
      "force": self.useForce(),
886 4717e6eb Michael Hanselmann
      "dry_run": self.dryRun(),
887 4717e6eb Michael Hanselmann
      "no_remember": bool(self._checkIntVariable("no_remember")),
888 4717e6eb Michael Hanselmann
      })
889 0c55c24b Oleksiy Mishchenko
890 0c55c24b Oleksiy Mishchenko
891 3175ade6 Michael Hanselmann
class R_2_instances_name_shutdown(baserlib.OpcodeResource):
892 0c55c24b Oleksiy Mishchenko
  """/2/instances/[instance_name]/shutdown resource.
893 0c55c24b Oleksiy Mishchenko

894 0c55c24b Oleksiy Mishchenko
  Implements an instance shutdown.
895 0c55c24b Oleksiy Mishchenko

896 0c55c24b Oleksiy Mishchenko
  """
897 3175ade6 Michael Hanselmann
  PUT_OPCODE = opcodes.OpInstanceShutdown
898 0c55c24b Oleksiy Mishchenko
899 3175ade6 Michael Hanselmann
  def GetPutOpInput(self):
900 3175ade6 Michael Hanselmann
    """Shutdown an instance.
901 7fa310f6 René Nussbaumer

902 0c55c24b Oleksiy Mishchenko
    """
903 3175ade6 Michael Hanselmann
    return (self.request_body, {
904 3175ade6 Michael Hanselmann
      "instance_name": self.items[0],
905 3175ade6 Michael Hanselmann
      "no_remember": bool(self._checkIntVariable("no_remember")),
906 3175ade6 Michael Hanselmann
      "dry_run": self.dryRun(),
907 3175ade6 Michael Hanselmann
      })
908 0c55c24b Oleksiy Mishchenko
909 0c55c24b Oleksiy Mishchenko
910 c744425f Michael Hanselmann
def _ParseInstanceReinstallRequest(name, data):
911 c744425f Michael Hanselmann
  """Parses a request for reinstalling an instance.
912 c744425f Michael Hanselmann

913 c744425f Michael Hanselmann
  """
914 c744425f Michael Hanselmann
  if not isinstance(data, dict):
915 c744425f Michael Hanselmann
    raise http.HttpBadRequest("Invalid body contents, not a dictionary")
916 c744425f Michael Hanselmann
917 bd0807fe Guido Trotter
  ostype = baserlib.CheckParameter(data, "os", default=None)
918 c744425f Michael Hanselmann
  start = baserlib.CheckParameter(data, "start", exptype=bool,
919 c744425f Michael Hanselmann
                                  default=True)
920 c744425f Michael Hanselmann
  osparams = baserlib.CheckParameter(data, "osparams", default=None)
921 c744425f Michael Hanselmann
922 c744425f Michael Hanselmann
  ops = [
923 ee3e37a7 Iustin Pop
    opcodes.OpInstanceShutdown(instance_name=name),
924 5073fd8f Iustin Pop
    opcodes.OpInstanceReinstall(instance_name=name, os_type=ostype,
925 c744425f Michael Hanselmann
                                osparams=osparams),
926 c744425f Michael Hanselmann
    ]
927 c744425f Michael Hanselmann
928 c744425f Michael Hanselmann
  if start:
929 c873d91c Iustin Pop
    ops.append(opcodes.OpInstanceStartup(instance_name=name, force=False))
930 c744425f Michael Hanselmann
931 c744425f Michael Hanselmann
  return ops
932 c744425f Michael Hanselmann
933 c744425f Michael Hanselmann
934 f6ce0ba2 Michael Hanselmann
class R_2_instances_name_reinstall(baserlib.OpcodeResource):
935 e5b7c4ca Iustin Pop
  """/2/instances/[instance_name]/reinstall resource.
936 e5b7c4ca Iustin Pop

937 e5b7c4ca Iustin Pop
  Implements an instance reinstall.
938 e5b7c4ca Iustin Pop

939 e5b7c4ca Iustin Pop
  """
940 f6ce0ba2 Michael Hanselmann
  POST_OPCODE = opcodes.OpInstanceReinstall
941 f6ce0ba2 Michael Hanselmann
942 e5b7c4ca Iustin Pop
  def POST(self):
943 e5b7c4ca Iustin Pop
    """Reinstall an instance.
944 e5b7c4ca Iustin Pop

945 e5b7c4ca Iustin Pop
    The URI takes os=name and nostartup=[0|1] optional
946 e5b7c4ca Iustin Pop
    parameters. By default, the instance will be started
947 e5b7c4ca Iustin Pop
    automatically.
948 e5b7c4ca Iustin Pop

949 e5b7c4ca Iustin Pop
    """
950 c744425f Michael Hanselmann
    if self.request_body:
951 c744425f Michael Hanselmann
      if self.queryargs:
952 c744425f Michael Hanselmann
        raise http.HttpBadRequest("Can't combine query and body parameters")
953 c744425f Michael Hanselmann
954 c744425f Michael Hanselmann
      body = self.request_body
955 bd0807fe Guido Trotter
    elif self.queryargs:
956 c744425f Michael Hanselmann
      # Legacy interface, do not modify/extend
957 c744425f Michael Hanselmann
      body = {
958 c744425f Michael Hanselmann
        "os": self._checkStringVariable("os"),
959 c744425f Michael Hanselmann
        "start": not self._checkIntVariable("nostartup"),
960 c744425f Michael Hanselmann
        }
961 bd0807fe Guido Trotter
    else:
962 bd0807fe Guido Trotter
      body = {}
963 c744425f Michael Hanselmann
964 c744425f Michael Hanselmann
    ops = _ParseInstanceReinstallRequest(self.items[0], body)
965 c744425f Michael Hanselmann
966 be1ddd09 Michael Hanselmann
    return self.SubmitJob(ops)
967 e5b7c4ca Iustin Pop
968 e5b7c4ca Iustin Pop
969 0dbc732c Michael Hanselmann
class R_2_instances_name_replace_disks(baserlib.OpcodeResource):
970 4c98b915 Michael Hanselmann
  """/2/instances/[instance_name]/replace-disks resource.
971 4c98b915 Michael Hanselmann

972 4c98b915 Michael Hanselmann
  """
973 0dbc732c Michael Hanselmann
  POST_OPCODE = opcodes.OpInstanceReplaceDisks
974 0dbc732c Michael Hanselmann
975 0dbc732c Michael Hanselmann
  def GetPostOpInput(self):
976 4c98b915 Michael Hanselmann
    """Replaces disks on an instance.
977 4c98b915 Michael Hanselmann

978 4c98b915 Michael Hanselmann
    """
979 0dbc732c Michael Hanselmann
    static = {
980 0dbc732c Michael Hanselmann
      "instance_name": self.items[0],
981 0dbc732c Michael Hanselmann
      }
982 4c98b915 Michael Hanselmann
983 539d65ba Michael Hanselmann
    if self.request_body:
984 7dcf333d Michael Hanselmann
      data = self.request_body
985 539d65ba Michael Hanselmann
    elif self.queryargs:
986 539d65ba Michael Hanselmann
      # Legacy interface, do not modify/extend
987 7dcf333d Michael Hanselmann
      data = {
988 539d65ba Michael Hanselmann
        "remote_node": self._checkStringVariable("remote_node", default=None),
989 539d65ba Michael Hanselmann
        "mode": self._checkStringVariable("mode", default=None),
990 539d65ba Michael Hanselmann
        "disks": self._checkStringVariable("disks", default=None),
991 539d65ba Michael Hanselmann
        "iallocator": self._checkStringVariable("iallocator", default=None),
992 539d65ba Michael Hanselmann
        }
993 539d65ba Michael Hanselmann
    else:
994 7dcf333d Michael Hanselmann
      data = {}
995 539d65ba Michael Hanselmann
996 0dbc732c Michael Hanselmann
    # Parse disks
997 0dbc732c Michael Hanselmann
    try:
998 7dcf333d Michael Hanselmann
      raw_disks = data.pop("disks")
999 0dbc732c Michael Hanselmann
    except KeyError:
1000 0dbc732c Michael Hanselmann
      pass
1001 0dbc732c Michael Hanselmann
    else:
1002 7dcf333d Michael Hanselmann
      if raw_disks:
1003 7dcf333d Michael Hanselmann
        if ht.TListOf(ht.TInt)(raw_disks): # pylint: disable=E1102
1004 7dcf333d Michael Hanselmann
          data["disks"] = raw_disks
1005 7dcf333d Michael Hanselmann
        else:
1006 7dcf333d Michael Hanselmann
          # Backwards compatibility for strings of the format "1, 2, 3"
1007 7dcf333d Michael Hanselmann
          try:
1008 7dcf333d Michael Hanselmann
            data["disks"] = [int(part) for part in raw_disks.split(",")]
1009 7dcf333d Michael Hanselmann
          except (TypeError, ValueError), err:
1010 7dcf333d Michael Hanselmann
            raise http.HttpBadRequest("Invalid disk index passed: %s" % err)
1011 0dbc732c Michael Hanselmann
1012 0dbc732c Michael Hanselmann
    return (data, static)
1013 4c98b915 Michael Hanselmann
1014 4c98b915 Michael Hanselmann
1015 1824a7a1 Michael Hanselmann
class R_2_instances_name_activate_disks(baserlib.OpcodeResource):
1016 2197b66f René Nussbaumer
  """/2/instances/[instance_name]/activate-disks resource.
1017 2197b66f René Nussbaumer

1018 2197b66f René Nussbaumer
  """
1019 1824a7a1 Michael Hanselmann
  PUT_OPCODE = opcodes.OpInstanceActivateDisks
1020 1824a7a1 Michael Hanselmann
1021 1824a7a1 Michael Hanselmann
  def GetPutOpInput(self):
1022 2197b66f René Nussbaumer
    """Activate disks for an instance.
1023 2197b66f René Nussbaumer

1024 2197b66f René Nussbaumer
    The URI might contain ignore_size to ignore current recorded size.
1025 2197b66f René Nussbaumer

1026 2197b66f René Nussbaumer
    """
1027 1824a7a1 Michael Hanselmann
    return ({}, {
1028 1824a7a1 Michael Hanselmann
      "instance_name": self.items[0],
1029 1824a7a1 Michael Hanselmann
      "ignore_size": bool(self._checkIntVariable("ignore_size")),
1030 1824a7a1 Michael Hanselmann
      })
1031 2197b66f René Nussbaumer
1032 2197b66f René Nussbaumer
1033 973ec124 Michael Hanselmann
class R_2_instances_name_deactivate_disks(baserlib.OpcodeResource):
1034 0a37de5f René Nussbaumer
  """/2/instances/[instance_name]/deactivate-disks resource.
1035 0a37de5f René Nussbaumer

1036 0a37de5f René Nussbaumer
  """
1037 973ec124 Michael Hanselmann
  PUT_OPCODE = opcodes.OpInstanceDeactivateDisks
1038 973ec124 Michael Hanselmann
1039 973ec124 Michael Hanselmann
  def GetPutOpInput(self):
1040 0a37de5f René Nussbaumer
    """Deactivate disks for an instance.
1041 0a37de5f René Nussbaumer

1042 0a37de5f René Nussbaumer
    """
1043 973ec124 Michael Hanselmann
    return ({}, {
1044 973ec124 Michael Hanselmann
      "instance_name": self.items[0],
1045 973ec124 Michael Hanselmann
      })
1046 0a37de5f René Nussbaumer
1047 0a37de5f René Nussbaumer
1048 a52978c7 Michael Hanselmann
class R_2_instances_name_recreate_disks(baserlib.OpcodeResource):
1049 a52978c7 Michael Hanselmann
  """/2/instances/[instance_name]/recreate-disks resource.
1050 a52978c7 Michael Hanselmann

1051 a52978c7 Michael Hanselmann
  """
1052 a52978c7 Michael Hanselmann
  POST_OPCODE = opcodes.OpInstanceRecreateDisks
1053 a52978c7 Michael Hanselmann
1054 a52978c7 Michael Hanselmann
  def GetPostOpInput(self):
1055 a52978c7 Michael Hanselmann
    """Recreate disks for an instance.
1056 a52978c7 Michael Hanselmann

1057 a52978c7 Michael Hanselmann
    """
1058 a52978c7 Michael Hanselmann
    return ({}, {
1059 a52978c7 Michael Hanselmann
      "instance_name": self.items[0],
1060 a52978c7 Michael Hanselmann
      })
1061 a52978c7 Michael Hanselmann
1062 a52978c7 Michael Hanselmann
1063 1c63311d Michael Hanselmann
class R_2_instances_name_prepare_export(baserlib.OpcodeResource):
1064 ebeb600f Michael Hanselmann
  """/2/instances/[instance_name]/prepare-export resource.
1065 ebeb600f Michael Hanselmann

1066 ebeb600f Michael Hanselmann
  """
1067 1c63311d Michael Hanselmann
  PUT_OPCODE = opcodes.OpBackupPrepare
1068 ebeb600f Michael Hanselmann
1069 1c63311d Michael Hanselmann
  def GetPutOpInput(self):
1070 1c63311d Michael Hanselmann
    """Prepares an export for an instance.
1071 ebeb600f Michael Hanselmann

1072 ebeb600f Michael Hanselmann
    """
1073 1c63311d Michael Hanselmann
    return ({}, {
1074 1c63311d Michael Hanselmann
      "instance_name": self.items[0],
1075 1c63311d Michael Hanselmann
      "mode": self._checkStringVariable("mode"),
1076 1c63311d Michael Hanselmann
      })
1077 ebeb600f Michael Hanselmann
1078 ebeb600f Michael Hanselmann
1079 134afbe7 Michael Hanselmann
class R_2_instances_name_export(baserlib.OpcodeResource):
1080 ebeb600f Michael Hanselmann
  """/2/instances/[instance_name]/export resource.
1081 ebeb600f Michael Hanselmann

1082 ebeb600f Michael Hanselmann
  """
1083 134afbe7 Michael Hanselmann
  PUT_OPCODE = opcodes.OpBackupExport
1084 134afbe7 Michael Hanselmann
  PUT_RENAME = {
1085 134afbe7 Michael Hanselmann
    "destination": "target_node",
1086 134afbe7 Michael Hanselmann
    }
1087 ebeb600f Michael Hanselmann
1088 134afbe7 Michael Hanselmann
  def GetPutOpInput(self):
1089 134afbe7 Michael Hanselmann
    """Exports an instance.
1090 ebeb600f Michael Hanselmann

1091 ebeb600f Michael Hanselmann
    """
1092 134afbe7 Michael Hanselmann
    return (self.request_body, {
1093 134afbe7 Michael Hanselmann
      "instance_name": self.items[0],
1094 134afbe7 Michael Hanselmann
      })
1095 ebeb600f Michael Hanselmann
1096 ebeb600f Michael Hanselmann
1097 075a29be Michael Hanselmann
class R_2_instances_name_migrate(baserlib.OpcodeResource):
1098 5823e0d2 Michael Hanselmann
  """/2/instances/[instance_name]/migrate resource.
1099 5823e0d2 Michael Hanselmann

1100 5823e0d2 Michael Hanselmann
  """
1101 075a29be Michael Hanselmann
  PUT_OPCODE = opcodes.OpInstanceMigrate
1102 5823e0d2 Michael Hanselmann
1103 075a29be Michael Hanselmann
  def GetPutOpInput(self):
1104 075a29be Michael Hanselmann
    """Migrates an instance.
1105 5823e0d2 Michael Hanselmann

1106 5823e0d2 Michael Hanselmann
    """
1107 075a29be Michael Hanselmann
    return (self.request_body, {
1108 075a29be Michael Hanselmann
      "instance_name": self.items[0],
1109 075a29be Michael Hanselmann
      })
1110 5823e0d2 Michael Hanselmann
1111 5823e0d2 Michael Hanselmann
1112 b5f2ab80 Michael Hanselmann
class R_2_instances_name_failover(baserlib.OpcodeResource):
1113 c0a146a1 Michael Hanselmann
  """/2/instances/[instance_name]/failover resource.
1114 c0a146a1 Michael Hanselmann

1115 c0a146a1 Michael Hanselmann
  """
1116 b5f2ab80 Michael Hanselmann
  PUT_OPCODE = opcodes.OpInstanceFailover
1117 c0a146a1 Michael Hanselmann
1118 b5f2ab80 Michael Hanselmann
  def GetPutOpInput(self):
1119 b5f2ab80 Michael Hanselmann
    """Does a failover of an instance.
1120 c0a146a1 Michael Hanselmann

1121 c0a146a1 Michael Hanselmann
    """
1122 b5f2ab80 Michael Hanselmann
    return (self.request_body, {
1123 c0a146a1 Michael Hanselmann
      "instance_name": self.items[0],
1124 c0a146a1 Michael Hanselmann
      })
1125 c0a146a1 Michael Hanselmann
1126 c0a146a1 Michael Hanselmann
1127 d76f9b5d Michael Hanselmann
class R_2_instances_name_rename(baserlib.OpcodeResource):
1128 d56e7dc7 Michael Hanselmann
  """/2/instances/[instance_name]/rename resource.
1129 d56e7dc7 Michael Hanselmann

1130 d56e7dc7 Michael Hanselmann
  """
1131 d76f9b5d Michael Hanselmann
  PUT_OPCODE = opcodes.OpInstanceRename
1132 d56e7dc7 Michael Hanselmann
1133 d76f9b5d Michael Hanselmann
  def GetPutOpInput(self):
1134 d76f9b5d Michael Hanselmann
    """Changes the name of an instance.
1135 d56e7dc7 Michael Hanselmann

1136 d56e7dc7 Michael Hanselmann
    """
1137 d76f9b5d Michael Hanselmann
    return (self.request_body, {
1138 d76f9b5d Michael Hanselmann
      "instance_name": self.items[0],
1139 d76f9b5d Michael Hanselmann
      })
1140 d56e7dc7 Michael Hanselmann
1141 d56e7dc7 Michael Hanselmann
1142 f3db88ba Michael Hanselmann
class R_2_instances_name_modify(baserlib.OpcodeResource):
1143 3882937a Michael Hanselmann
  """/2/instances/[instance_name]/modify resource.
1144 3882937a Michael Hanselmann

1145 3882937a Michael Hanselmann
  """
1146 f3db88ba Michael Hanselmann
  PUT_OPCODE = opcodes.OpInstanceSetParams
1147 3882937a Michael Hanselmann
1148 f3db88ba Michael Hanselmann
  def GetPutOpInput(self):
1149 f3db88ba Michael Hanselmann
    """Changes parameters of an instance.
1150 3882937a Michael Hanselmann

1151 3882937a Michael Hanselmann
    """
1152 f3db88ba Michael Hanselmann
    return (self.request_body, {
1153 f3db88ba Michael Hanselmann
      "instance_name": self.items[0],
1154 f3db88ba Michael Hanselmann
      })
1155 3882937a Michael Hanselmann
1156 3882937a Michael Hanselmann
1157 335965b9 Michael Hanselmann
class R_2_instances_name_disk_grow(baserlib.OpcodeResource):
1158 b58a4d16 Michael Hanselmann
  """/2/instances/[instance_name]/disk/[disk_index]/grow resource.
1159 e23881ed Michael Hanselmann

1160 e23881ed Michael Hanselmann
  """
1161 335965b9 Michael Hanselmann
  POST_OPCODE = opcodes.OpInstanceGrowDisk
1162 e23881ed Michael Hanselmann
1163 335965b9 Michael Hanselmann
  def GetPostOpInput(self):
1164 335965b9 Michael Hanselmann
    """Increases the size of an instance disk.
1165 e23881ed Michael Hanselmann

1166 e23881ed Michael Hanselmann
    """
1167 335965b9 Michael Hanselmann
    return (self.request_body, {
1168 e23881ed Michael Hanselmann
      "instance_name": self.items[0],
1169 e23881ed Michael Hanselmann
      "disk": int(self.items[1]),
1170 e23881ed Michael Hanselmann
      })
1171 e23881ed Michael Hanselmann
1172 e23881ed Michael Hanselmann
1173 26ff6ee2 Michael Hanselmann
class R_2_instances_name_console(baserlib.ResourceBase):
1174 b82d4c5e Michael Hanselmann
  """/2/instances/[instance_name]/console resource.
1175 b82d4c5e Michael Hanselmann

1176 b82d4c5e Michael Hanselmann
  """
1177 b82d4c5e Michael Hanselmann
  GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
1178 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpInstanceConsole
1179 b82d4c5e Michael Hanselmann
1180 b82d4c5e Michael Hanselmann
  def GET(self):
1181 b82d4c5e Michael Hanselmann
    """Request information for connecting to instance's console.
1182 b82d4c5e Michael Hanselmann

1183 b82d4c5e Michael Hanselmann
    @return: Serialized instance console description, see
1184 b82d4c5e Michael Hanselmann
             L{objects.InstanceConsole}
1185 b82d4c5e Michael Hanselmann

1186 b82d4c5e Michael Hanselmann
    """
1187 be1ddd09 Michael Hanselmann
    client = self.GetClient()
1188 b82d4c5e Michael Hanselmann
1189 b82d4c5e Michael Hanselmann
    ((console, ), ) = client.QueryInstances([self.items[0]], ["console"], False)
1190 b82d4c5e Michael Hanselmann
1191 b82d4c5e Michael Hanselmann
    if console is None:
1192 b82d4c5e Michael Hanselmann
      raise http.HttpServiceUnavailable("Instance console unavailable")
1193 b82d4c5e Michael Hanselmann
1194 b82d4c5e Michael Hanselmann
    assert isinstance(console, dict)
1195 b82d4c5e Michael Hanselmann
    return console
1196 b82d4c5e Michael Hanselmann
1197 b82d4c5e Michael Hanselmann
1198 208a6cff Michael Hanselmann
def _GetQueryFields(args):
1199 208a6cff Michael Hanselmann
  """
1200 208a6cff Michael Hanselmann

1201 208a6cff Michael Hanselmann
  """
1202 208a6cff Michael Hanselmann
  try:
1203 208a6cff Michael Hanselmann
    fields = args["fields"]
1204 208a6cff Michael Hanselmann
  except KeyError:
1205 208a6cff Michael Hanselmann
    raise http.HttpBadRequest("Missing 'fields' query argument")
1206 208a6cff Michael Hanselmann
1207 208a6cff Michael Hanselmann
  return _SplitQueryFields(fields[0])
1208 208a6cff Michael Hanselmann
1209 208a6cff Michael Hanselmann
1210 208a6cff Michael Hanselmann
def _SplitQueryFields(fields):
1211 208a6cff Michael Hanselmann
  """
1212 208a6cff Michael Hanselmann

1213 208a6cff Michael Hanselmann
  """
1214 208a6cff Michael Hanselmann
  return [i.strip() for i in fields.split(",")]
1215 208a6cff Michael Hanselmann
1216 208a6cff Michael Hanselmann
1217 26ff6ee2 Michael Hanselmann
class R_2_query(baserlib.ResourceBase):
1218 208a6cff Michael Hanselmann
  """/2/query/[resource] resource.
1219 208a6cff Michael Hanselmann

1220 208a6cff Michael Hanselmann
  """
1221 208a6cff Michael Hanselmann
  # Results might contain sensitive information
1222 208a6cff Michael Hanselmann
  GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
1223 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpQuery
1224 f6ce0ba2 Michael Hanselmann
  PUT_OPCODE = opcodes.OpQuery
1225 208a6cff Michael Hanselmann
1226 2e5c33db Iustin Pop
  def _Query(self, fields, qfilter):
1227 2e5c33db Iustin Pop
    return self.GetClient().Query(self.items[0], fields, qfilter).ToDict()
1228 208a6cff Michael Hanselmann
1229 208a6cff Michael Hanselmann
  def GET(self):
1230 208a6cff Michael Hanselmann
    """Returns resource information.
1231 208a6cff Michael Hanselmann

1232 208a6cff Michael Hanselmann
    @return: Query result, see L{objects.QueryResponse}
1233 208a6cff Michael Hanselmann

1234 208a6cff Michael Hanselmann
    """
1235 208a6cff Michael Hanselmann
    return self._Query(_GetQueryFields(self.queryargs), None)
1236 208a6cff Michael Hanselmann
1237 208a6cff Michael Hanselmann
  def PUT(self):
1238 208a6cff Michael Hanselmann
    """Submits job querying for resources.
1239 208a6cff Michael Hanselmann

1240 208a6cff Michael Hanselmann
    @return: Query result, see L{objects.QueryResponse}
1241 208a6cff Michael Hanselmann

1242 208a6cff Michael Hanselmann
    """
1243 208a6cff Michael Hanselmann
    body = self.request_body
1244 208a6cff Michael Hanselmann
1245 208a6cff Michael Hanselmann
    baserlib.CheckType(body, dict, "Body contents")
1246 208a6cff Michael Hanselmann
1247 208a6cff Michael Hanselmann
    try:
1248 208a6cff Michael Hanselmann
      fields = body["fields"]
1249 208a6cff Michael Hanselmann
    except KeyError:
1250 208a6cff Michael Hanselmann
      fields = _GetQueryFields(self.queryargs)
1251 208a6cff Michael Hanselmann
1252 2e5c33db Iustin Pop
    qfilter = body.get("qfilter", None)
1253 2e5c33db Iustin Pop
    # TODO: remove this after 2.7
1254 2e5c33db Iustin Pop
    if qfilter is None:
1255 2e5c33db Iustin Pop
      qfilter = body.get("filter", None)
1256 2e5c33db Iustin Pop
1257 2e5c33db Iustin Pop
    return self._Query(fields, qfilter)
1258 208a6cff Michael Hanselmann
1259 208a6cff Michael Hanselmann
1260 26ff6ee2 Michael Hanselmann
class R_2_query_fields(baserlib.ResourceBase):
1261 208a6cff Michael Hanselmann
  """/2/query/[resource]/fields resource.
1262 208a6cff Michael Hanselmann

1263 208a6cff Michael Hanselmann
  """
1264 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpQueryFields
1265 f6ce0ba2 Michael Hanselmann
1266 208a6cff Michael Hanselmann
  def GET(self):
1267 208a6cff Michael Hanselmann
    """Retrieves list of available fields for a resource.
1268 208a6cff Michael Hanselmann

1269 208a6cff Michael Hanselmann
    @return: List of serialized L{objects.QueryFieldDefinition}
1270 208a6cff Michael Hanselmann

1271 208a6cff Michael Hanselmann
    """
1272 208a6cff Michael Hanselmann
    try:
1273 208a6cff Michael Hanselmann
      raw_fields = self.queryargs["fields"]
1274 208a6cff Michael Hanselmann
    except KeyError:
1275 208a6cff Michael Hanselmann
      fields = None
1276 208a6cff Michael Hanselmann
    else:
1277 208a6cff Michael Hanselmann
      fields = _SplitQueryFields(raw_fields[0])
1278 208a6cff Michael Hanselmann
1279 be1ddd09 Michael Hanselmann
    return self.GetClient().QueryFields(self.items[0], fields).ToDict()
1280 208a6cff Michael Hanselmann
1281 208a6cff Michael Hanselmann
1282 460ef073 Michael Hanselmann
class _R_Tags(baserlib.OpcodeResource):
1283 18cb43a2 Oleksiy Mishchenko
  """ Quasiclass for tagging resources
1284 441e7cfd Oleksiy Mishchenko

1285 c8e0a534 Iustin Pop
  Manages tags. When inheriting this class you must define the
1286 18cb43a2 Oleksiy Mishchenko
  TAG_LEVEL for it.
1287 441e7cfd Oleksiy Mishchenko

1288 441e7cfd Oleksiy Mishchenko
  """
1289 7a6b9510 Iustin Pop
  TAG_LEVEL = None
1290 f6ce0ba2 Michael Hanselmann
  GET_OPCODE = opcodes.OpTagsGet
1291 460ef073 Michael Hanselmann
  PUT_OPCODE = opcodes.OpTagsSet
1292 460ef073 Michael Hanselmann
  DELETE_OPCODE = opcodes.OpTagsDel
1293 18cb43a2 Oleksiy Mishchenko
1294 460ef073 Michael Hanselmann
  def __init__(self, items, queryargs, req, **kwargs):
1295 18cb43a2 Oleksiy Mishchenko
    """A tag resource constructor.
1296 18cb43a2 Oleksiy Mishchenko

1297 18cb43a2 Oleksiy Mishchenko
    We have to override the default to sort out cluster naming case.
1298 18cb43a2 Oleksiy Mishchenko

1299 18cb43a2 Oleksiy Mishchenko
    """
1300 460ef073 Michael Hanselmann
    baserlib.OpcodeResource.__init__(self, items, queryargs, req, **kwargs)
1301 18cb43a2 Oleksiy Mishchenko
1302 5313eed7 Michael Hanselmann
    if self.TAG_LEVEL == constants.TAG_CLUSTER:
1303 5313eed7 Michael Hanselmann
      self.name = None
1304 18cb43a2 Oleksiy Mishchenko
    else:
1305 5313eed7 Michael Hanselmann
      self.name = items[0]
1306 441e7cfd Oleksiy Mishchenko
1307 441e7cfd Oleksiy Mishchenko
  def GET(self):
1308 18cb43a2 Oleksiy Mishchenko
    """Returns a list of tags.
1309 441e7cfd Oleksiy Mishchenko

1310 441e7cfd Oleksiy Mishchenko
    Example: ["tag1", "tag2", "tag3"]
1311 441e7cfd Oleksiy Mishchenko

1312 441e7cfd Oleksiy Mishchenko
    """
1313 f87ec53f Michael Hanselmann
    kind = self.TAG_LEVEL
1314 f87ec53f Michael Hanselmann
1315 f87ec53f Michael Hanselmann
    if kind in (constants.TAG_INSTANCE,
1316 f87ec53f Michael Hanselmann
                constants.TAG_NODEGROUP,
1317 f87ec53f Michael Hanselmann
                constants.TAG_NODE):
1318 f87ec53f Michael Hanselmann
      if not self.name:
1319 f87ec53f Michael Hanselmann
        raise http.HttpBadRequest("Missing name on tag request")
1320 f87ec53f Michael Hanselmann
1321 be1ddd09 Michael Hanselmann
      cl = self.GetClient()
1322 f87ec53f Michael Hanselmann
      if kind == constants.TAG_INSTANCE:
1323 f87ec53f Michael Hanselmann
        fn = cl.QueryInstances
1324 f87ec53f Michael Hanselmann
      elif kind == constants.TAG_NODEGROUP:
1325 f87ec53f Michael Hanselmann
        fn = cl.QueryGroups
1326 f87ec53f Michael Hanselmann
      else:
1327 f87ec53f Michael Hanselmann
        fn = cl.QueryNodes
1328 f87ec53f Michael Hanselmann
      result = fn(names=[self.name], fields=["tags"], use_locking=False)
1329 f87ec53f Michael Hanselmann
      if not result or not result[0]:
1330 f87ec53f Michael Hanselmann
        raise http.HttpBadGateway("Invalid response from tag query")
1331 f87ec53f Michael Hanselmann
      tags = result[0][0]
1332 f87ec53f Michael Hanselmann
1333 f87ec53f Michael Hanselmann
    elif kind == constants.TAG_CLUSTER:
1334 f87ec53f Michael Hanselmann
      assert not self.name
1335 f87ec53f Michael Hanselmann
      # TODO: Use query API?
1336 f87ec53f Michael Hanselmann
      ssc = ssconf.SimpleStore()
1337 f87ec53f Michael Hanselmann
      tags = ssc.GetClusterTags()
1338 f87ec53f Michael Hanselmann
1339 f87ec53f Michael Hanselmann
    return list(tags)
1340 441e7cfd Oleksiy Mishchenko
1341 460ef073 Michael Hanselmann
  def GetPutOpInput(self):
1342 18cb43a2 Oleksiy Mishchenko
    """Add a set of tags.
1343 441e7cfd Oleksiy Mishchenko

1344 c41eea6e Iustin Pop
    The request as a list of strings should be PUT to this URI. And
1345 c41eea6e Iustin Pop
    you'll have back a job id.
1346 441e7cfd Oleksiy Mishchenko

1347 441e7cfd Oleksiy Mishchenko
    """
1348 460ef073 Michael Hanselmann
    return ({}, {
1349 460ef073 Michael Hanselmann
      "kind": self.TAG_LEVEL,
1350 460ef073 Michael Hanselmann
      "name": self.name,
1351 460ef073 Michael Hanselmann
      "tags": self.queryargs.get("tag", []),
1352 460ef073 Michael Hanselmann
      "dry_run": self.dryRun(),
1353 460ef073 Michael Hanselmann
      })
1354 15fd9fd5 Oleksiy Mishchenko
1355 460ef073 Michael Hanselmann
  def GetDeleteOpInput(self):
1356 15fd9fd5 Oleksiy Mishchenko
    """Delete a tag.
1357 15fd9fd5 Oleksiy Mishchenko

1358 18cb43a2 Oleksiy Mishchenko
    In order to delete a set of tags, the DELETE
1359 c41eea6e Iustin Pop
    request should be addressed to URI like:
1360 18cb43a2 Oleksiy Mishchenko
    /tags?tag=[tag]&tag=[tag]
1361 15fd9fd5 Oleksiy Mishchenko

1362 15fd9fd5 Oleksiy Mishchenko
    """
1363 460ef073 Michael Hanselmann
    # Re-use code
1364 460ef073 Michael Hanselmann
    return self.GetPutOpInput()
1365 18cb43a2 Oleksiy Mishchenko
1366 18cb43a2 Oleksiy Mishchenko
1367 18cb43a2 Oleksiy Mishchenko
class R_2_instances_name_tags(_R_Tags):
1368 18cb43a2 Oleksiy Mishchenko
  """ /2/instances/[instance_name]/tags resource.
1369 18cb43a2 Oleksiy Mishchenko

1370 18cb43a2 Oleksiy Mishchenko
  Manages per-instance tags.
1371 18cb43a2 Oleksiy Mishchenko

1372 18cb43a2 Oleksiy Mishchenko
  """
1373 18cb43a2 Oleksiy Mishchenko
  TAG_LEVEL = constants.TAG_INSTANCE
1374 18cb43a2 Oleksiy Mishchenko
1375 18cb43a2 Oleksiy Mishchenko
1376 18cb43a2 Oleksiy Mishchenko
class R_2_nodes_name_tags(_R_Tags):
1377 18cb43a2 Oleksiy Mishchenko
  """ /2/nodes/[node_name]/tags resource.
1378 18cb43a2 Oleksiy Mishchenko

1379 18cb43a2 Oleksiy Mishchenko
  Manages per-node tags.
1380 18cb43a2 Oleksiy Mishchenko

1381 18cb43a2 Oleksiy Mishchenko
  """
1382 18cb43a2 Oleksiy Mishchenko
  TAG_LEVEL = constants.TAG_NODE
1383 18cb43a2 Oleksiy Mishchenko
1384 18cb43a2 Oleksiy Mishchenko
1385 414ebaf1 Michael Hanselmann
class R_2_groups_name_tags(_R_Tags):
1386 414ebaf1 Michael Hanselmann
  """ /2/groups/[group_name]/tags resource.
1387 414ebaf1 Michael Hanselmann

1388 414ebaf1 Michael Hanselmann
  Manages per-nodegroup tags.
1389 414ebaf1 Michael Hanselmann

1390 414ebaf1 Michael Hanselmann
  """
1391 414ebaf1 Michael Hanselmann
  TAG_LEVEL = constants.TAG_NODEGROUP
1392 414ebaf1 Michael Hanselmann
1393 414ebaf1 Michael Hanselmann
1394 18cb43a2 Oleksiy Mishchenko
class R_2_tags(_R_Tags):
1395 b58a4d16 Michael Hanselmann
  """ /2/tags resource.
1396 18cb43a2 Oleksiy Mishchenko

1397 18cb43a2 Oleksiy Mishchenko
  Manages cluster tags.
1398 18cb43a2 Oleksiy Mishchenko

1399 18cb43a2 Oleksiy Mishchenko
  """
1400 18cb43a2 Oleksiy Mishchenko
  TAG_LEVEL = constants.TAG_CLUSTER