Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / rlib2.py @ 3bd0f3d8

History | View | Annotate | Download (34.2 kB)

1 10b207d4 Oleksiy Mishchenko
#
2 10b207d4 Oleksiy Mishchenko
#
3 10b207d4 Oleksiy Mishchenko
4 37e61a77 Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 37e61a77 Iustin Pop
            "ndparams",
90 fd254195 Iustin Pop
            "group.uuid",
91 7118a0df Iustin Pop
            ] + _COMMON_FIELDS
92 4e5a68f8 Oleksiy Mishchenko
93 f4e86448 Michael Hanselmann
G_FIELDS = [
94 f4e86448 Michael Hanselmann
  "alloc_policy",
95 f4e86448 Michael Hanselmann
  "name",
96 f4e86448 Michael Hanselmann
  "node_cnt",
97 f4e86448 Michael Hanselmann
  "node_list",
98 edd49f9b Agata Murawska
  "ipolicy",
99 f4e86448 Michael Hanselmann
  ] + _COMMON_FIELDS
100 0897dc97 Adeodato Simo
101 bd7b2070 Michael Hanselmann
J_FIELDS_BULK = [
102 e987f166 Michael Hanselmann
  "id", "ops", "status", "summary",
103 bd7b2070 Michael Hanselmann
  "opstatus",
104 e987f166 Michael Hanselmann
  "received_ts", "start_ts", "end_ts",
105 e987f166 Michael Hanselmann
  ]
106 e987f166 Michael Hanselmann
107 bd7b2070 Michael Hanselmann
J_FIELDS = J_FIELDS_BULK + [
108 bd7b2070 Michael Hanselmann
  "oplog",
109 bd7b2070 Michael Hanselmann
  "opresult",
110 bd7b2070 Michael Hanselmann
  ]
111 bd7b2070 Michael Hanselmann
112 64dae8fc Michael Hanselmann
_NR_DRAINED = "drained"
113 51cc8637 Michael Hanselmann
_NR_MASTER_CANDIDATE = "master-candidate"
114 64dae8fc Michael Hanselmann
_NR_MASTER = "master"
115 64dae8fc Michael Hanselmann
_NR_OFFLINE = "offline"
116 64dae8fc Michael Hanselmann
_NR_REGULAR = "regular"
117 64dae8fc Michael Hanselmann
118 64dae8fc Michael Hanselmann
_NR_MAP = {
119 1e28e3b8 Michael Hanselmann
  constants.NR_MASTER: _NR_MASTER,
120 51cc8637 Michael Hanselmann
  constants.NR_MCANDIDATE: _NR_MASTER_CANDIDATE,
121 1e28e3b8 Michael Hanselmann
  constants.NR_DRAINED: _NR_DRAINED,
122 1e28e3b8 Michael Hanselmann
  constants.NR_OFFLINE: _NR_OFFLINE,
123 1e28e3b8 Michael Hanselmann
  constants.NR_REGULAR: _NR_REGULAR,
124 64dae8fc Michael Hanselmann
  }
125 64dae8fc Michael Hanselmann
126 1e28e3b8 Michael Hanselmann
assert frozenset(_NR_MAP.keys()) == constants.NR_ALL
127 1e28e3b8 Michael Hanselmann
128 d975f482 Michael Hanselmann
# Request data version field
129 d975f482 Michael Hanselmann
_REQ_DATA_VERSION = "__version__"
130 d975f482 Michael Hanselmann
131 6395cebb Michael Hanselmann
# Feature string for instance creation request data version 1
132 6395cebb Michael Hanselmann
_INST_CREATE_REQV1 = "instance-create-reqv1"
133 6395cebb Michael Hanselmann
134 c744425f Michael Hanselmann
# Feature string for instance reinstall request version 1
135 c744425f Michael Hanselmann
_INST_REINSTALL_REQV1 = "instance-reinstall-reqv1"
136 c744425f Michael Hanselmann
137 b7a1c816 Michael Hanselmann
# Feature string for node migration version 1
138 b7a1c816 Michael Hanselmann
_NODE_MIGRATE_REQV1 = "node-migrate-reqv1"
139 b7a1c816 Michael Hanselmann
140 de40437a Michael Hanselmann
# Feature string for node evacuation with LU-generated jobs
141 de40437a Michael Hanselmann
_NODE_EVAC_RES1 = "node-evac-res1"
142 de40437a Michael Hanselmann
143 b4fcee5b Michael Hanselmann
ALL_FEATURES = frozenset([
144 b4fcee5b Michael Hanselmann
  _INST_CREATE_REQV1,
145 b4fcee5b Michael Hanselmann
  _INST_REINSTALL_REQV1,
146 b4fcee5b Michael Hanselmann
  _NODE_MIGRATE_REQV1,
147 b4fcee5b Michael Hanselmann
  _NODE_EVAC_RES1,
148 b4fcee5b Michael Hanselmann
  ])
149 b4fcee5b Michael Hanselmann
150 793a8f7c Michael Hanselmann
# Timeout for /2/jobs/[job_id]/wait. Gives job up to 10 seconds to change.
151 793a8f7c Michael Hanselmann
_WFJC_TIMEOUT = 10
152 793a8f7c Michael Hanselmann
153 4e5a68f8 Oleksiy Mishchenko
154 26ff6ee2 Michael Hanselmann
class R_root(baserlib.ResourceBase):
155 0f945c65 Michael Hanselmann
  """/ resource.
156 0f945c65 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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