Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / rlib2.py @ 6f59b964

History | View | Annotate | Download (12.2 kB)

1
#
2
#
3

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

    
21

    
22
"""Remote API version 2 baserlib.library.
23

24
"""
25

    
26
from ganeti import opcodes
27
from ganeti import http
28
from ganeti import constants
29
from ganeti import cli
30
from ganeti.rapi import baserlib
31

    
32

    
33

    
34
I_FIELDS = ["name", "admin_state", "os",
35
            "pnode", "snodes",
36
            "disk_template",
37
            "nic.ips", "nic.macs", "nic.modes", "nic.links",
38
            "network_port",
39
            "disk.sizes", "disk_usage",
40
            "beparams", "hvparams",
41
            "oper_state", "oper_ram", "status",
42
            "tags"]
43

    
44
N_FIELDS = ["name", "offline", "master_candidate", "drained",
45
            "dtotal", "dfree",
46
            "mtotal", "mnode", "mfree",
47
            "pinst_cnt", "sinst_cnt", "tags",
48
            "ctotal", "cnodes", "csockets",
49
            ]
50

    
51

    
52
class R_version(baserlib.R_Generic):
53
  """/version resource.
54

55
  This resource should be used to determine the remote API version and
56
  to adapt clients accordingly.
57

58
  """
59
  def GET(self):
60
    """Returns the remote API version.
61

62
    """
63
    return constants.RAPI_VERSION
64

    
65

    
66
class R_2_info(baserlib.R_Generic):
67
  """Cluster info.
68

69
  """
70
  def GET(self):
71
    """Returns cluster information.
72

73
    """
74
    client = baserlib.GetClient()
75
    return client.QueryClusterInfo()
76

    
77

    
78
class R_2_os(baserlib.R_Generic):
79
  """/2/os resource.
80

81
  """
82
  def GET(self):
83
    """Return a list of all OSes.
84

85
    Can return error 500 in case of a problem.
86

87
    Example: ["debian-etch"]
88

89
    """
90
    cl = baserlib.GetClient()
91
    op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
92
    job_id = baserlib.SubmitJob([op], cl)
93
    # we use custom feedback function, instead of print we log the status
94
    result = cli.PollJob(job_id, cl, feedback_fn=baserlib.FeedbackFn)
95
    diagnose_data = result[0]
96

    
97
    if not isinstance(diagnose_data, list):
98
      raise http.HttpBadGateway(message="Can't get OS list")
99

    
100
    return [row[0] for row in diagnose_data if row[1]]
101

    
102

    
103
class R_2_jobs(baserlib.R_Generic):
104
  """/2/jobs resource.
105

106
  """
107
  def GET(self):
108
    """Returns a dictionary of jobs.
109

110
    @return: a dictionary with jobs id and uri.
111

112
    """
113
    fields = ["id"]
114
    cl = baserlib.GetClient()
115
    # Convert the list of lists to the list of ids
116
    result = [job_id for [job_id] in cl.QueryJobs(None, fields)]
117
    return baserlib.BuildUriList(result, "/2/jobs/%s",
118
                                 uri_fields=("id", "uri"))
119

    
120

    
121
class R_2_jobs_id(baserlib.R_Generic):
122
  """/2/jobs/[job_id] resource.
123

124
  """
125
  def GET(self):
126
    """Returns a job status.
127

128
    @return: a dictionary with job parameters.
129
        The result includes:
130
            - id: job ID as a number
131
            - status: current job status as a string
132
            - ops: involved OpCodes as a list of dictionaries for each
133
              opcodes in the job
134
            - opstatus: OpCodes status as a list
135
            - opresult: OpCodes results as a list of lists
136

137
    """
138
    fields = ["id", "ops", "status", "summary",
139
              "opstatus", "opresult", "oplog",
140
              "received_ts", "start_ts", "end_ts",
141
              ]
142
    job_id = self.items[0]
143
    result = baserlib.GetClient().QueryJobs([job_id, ], fields)[0]
144
    if result is None:
145
      raise http.HttpNotFound()
146
    return baserlib.MapFields(fields, result)
147

    
148
  def DELETE(self):
149
    """Cancel not-yet-started job.
150

151
    """
152
    job_id = self.items[0]
153
    result = baserlib.GetClient().CancelJob(job_id)
154
    return result
155

    
156

    
157
class R_2_nodes(baserlib.R_Generic):
158
  """/2/nodes resource.
159

160
  """
161
  def GET(self):
162
    """Returns a list of all nodes.
163

164
    """
165
    client = baserlib.GetClient()
166

    
167
    if self.useBulk():
168
      bulkdata = client.QueryNodes([], N_FIELDS, False)
169
      return baserlib.MapBulkFields(bulkdata, N_FIELDS)
170
    else:
171
      nodesdata = client.QueryNodes([], ["name"], False)
172
      nodeslist = [row[0] for row in nodesdata]
173
      return baserlib.BuildUriList(nodeslist, "/2/nodes/%s",
174
                                   uri_fields=("id", "uri"))
175

    
176

    
177
class R_2_nodes_name(baserlib.R_Generic):
178
  """/2/nodes/[node_name] resources.
179

180
  """
181
  def GET(self):
182
    """Send information about a node.
183

184
    """
185
    node_name = self.items[0]
186
    client = baserlib.GetClient()
187
    result = client.QueryNodes(names=[node_name], fields=N_FIELDS,
188
                               use_locking=self.useLocking())
189

    
190
    return baserlib.MapFields(N_FIELDS, result[0])
191

    
192

    
193
class R_2_instances(baserlib.R_Generic):
194
  """/2/instances resource.
195

196
  """
197
  def GET(self):
198
    """Returns a list of all available instances.
199

200
    """
201
    client = baserlib.GetClient()
202

    
203
    use_locking = self.useLocking()
204
    if self.useBulk():
205
      bulkdata = client.QueryInstances([], I_FIELDS, use_locking)
206
      return baserlib.MapBulkFields(bulkdata, I_FIELDS)
207
    else:
208
      instancesdata = client.QueryInstances([], ["name"], use_locking)
209
      instanceslist = [row[0] for row in instancesdata]
210
      return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
211
                                   uri_fields=("id", "uri"))
212

    
213
  def POST(self):
214
    """Create an instance.
215

216
    @return: a job id
217

218
    """
219
    if not isinstance(self.req.request_body, dict):
220
      raise http.HttpBadRequest("Invalid body contents, not a dictionary")
221

    
222
    beparams = baserlib.MakeParamsDict(self.req.request_body,
223
                                       constants.BES_PARAMETERS)
224
    hvparams = baserlib.MakeParamsDict(self.req.request_body,
225
                                       constants.HVS_PARAMETERS)
226
    fn = self.getBodyParameter
227

    
228
    # disk processing
229
    disk_data = fn('disks')
230
    if not isinstance(disk_data, list):
231
      raise http.HttpBadRequest("The 'disks' parameter should be a list")
232
    disks = []
233
    for idx, d in enumerate(disk_data):
234
      if not isinstance(d, int):
235
        raise http.HttpBadRequest("Disk %d specification wrong: should"
236
                                  " be an integer")
237
      disks.append({"size": d})
238
    # nic processing (one nic only)
239
    nics = [{"mac": fn("mac", constants.VALUE_AUTO)}]
240
    if fn("ip", None) is not None:
241
      nics[0]["ip"] = fn("ip")
242
    if fn("mode", None) is not None:
243
      nics[0]["mode"] = fn("mode")
244
    if fn("link", None) is not None:
245
      nics[0]["link"] = fn("link")
246
    if fn("bridge", None) is not None:
247
       nics[0]["bridge"] = fn("bridge")
248

    
249
    op = opcodes.OpCreateInstance(
250
      mode=constants.INSTANCE_CREATE,
251
      instance_name=fn('name'),
252
      disks=disks,
253
      disk_template=fn('disk_template'),
254
      os_type=fn('os'),
255
      pnode=fn('pnode', None),
256
      snode=fn('snode', None),
257
      iallocator=fn('iallocator', None),
258
      nics=nics,
259
      start=fn('start', True),
260
      ip_check=fn('ip_check', True),
261
      wait_for_sync=True,
262
      hypervisor=fn('hypervisor', None),
263
      hvparams=hvparams,
264
      beparams=beparams,
265
      file_storage_dir=fn('file_storage_dir', None),
266
      file_driver=fn('file_driver', 'loop'),
267
      dry_run=bool(self.dryRun()),
268
      )
269

    
270
    return baserlib.SubmitJob([op])
271

    
272

    
273
class R_2_instances_name(baserlib.R_Generic):
274
  """/2/instances/[instance_name] resources.
275

276
  """
277
  def GET(self):
278
    """Send information about an instance.
279

280
    """
281
    client = baserlib.GetClient()
282
    instance_name = self.items[0]
283
    result = client.QueryInstances(names=[instance_name], fields=I_FIELDS,
284
                                   use_locking=self.useLocking())
285

    
286
    return baserlib.MapFields(I_FIELDS, result[0])
287

    
288
  def DELETE(self):
289
    """Delete an instance.
290

291
    """
292
    op = opcodes.OpRemoveInstance(instance_name=self.items[0],
293
                                  ignore_failures=False,
294
                                  dry_run=bool(self.dryRun()))
295
    return baserlib.SubmitJob([op])
296

    
297

    
298
class R_2_instances_name_reboot(baserlib.R_Generic):
299
  """/2/instances/[instance_name]/reboot resource.
300

301
  Implements an instance reboot.
302

303
  """
304
  def POST(self):
305
    """Reboot an instance.
306

307
    The URI takes type=[hard|soft|full] and
308
    ignore_secondaries=[False|True] parameters.
309

310
    """
311
    instance_name = self.items[0]
312
    reboot_type = self.queryargs.get('type',
313
                                     [constants.INSTANCE_REBOOT_HARD])[0]
314
    ignore_secondaries = bool(self.queryargs.get('ignore_secondaries',
315
                                                 [False])[0])
316
    op = opcodes.OpRebootInstance(instance_name=instance_name,
317
                                  reboot_type=reboot_type,
318
                                  ignore_secondaries=ignore_secondaries,
319
                                  dry_run=bool(self.dryRun()))
320

    
321
    return baserlib.SubmitJob([op])
322

    
323

    
324
class R_2_instances_name_startup(baserlib.R_Generic):
325
  """/2/instances/[instance_name]/startup resource.
326

327
  Implements an instance startup.
328

329
  """
330
  def PUT(self):
331
    """Startup an instance.
332

333
    The URI takes force=[False|True] parameter to start the instance
334
    if even if secondary disks are failing.
335

336
    """
337
    instance_name = self.items[0]
338
    force_startup = bool(self.queryargs.get('force', [False])[0])
339
    op = opcodes.OpStartupInstance(instance_name=instance_name,
340
                                   force=force_startup,
341
                                   dry_run=bool(self.dryRun()))
342

    
343
    return baserlib.SubmitJob([op])
344

    
345

    
346
class R_2_instances_name_shutdown(baserlib.R_Generic):
347
  """/2/instances/[instance_name]/shutdown resource.
348

349
  Implements an instance shutdown.
350

351
  """
352
  def PUT(self):
353
    """Shutdown an instance.
354

355
    """
356
    instance_name = self.items[0]
357
    op = opcodes.OpShutdownInstance(instance_name=instance_name,
358
                                    dry_run=bool(self.dryRun()))
359

    
360
    return baserlib.SubmitJob([op])
361

    
362

    
363
class _R_Tags(baserlib.R_Generic):
364
  """ Quasiclass for tagging resources
365

366
  Manages tags. When inheriting this class you must define the
367
  TAG_LEVEL for it.
368

369
  """
370
  TAG_LEVEL = None
371

    
372
  def __init__(self, items, queryargs, req):
373
    """A tag resource constructor.
374

375
    We have to override the default to sort out cluster naming case.
376

377
    """
378
    baserlib.R_Generic.__init__(self, items, queryargs, req)
379

    
380
    if self.TAG_LEVEL != constants.TAG_CLUSTER:
381
      self.name = items[0]
382
    else:
383
      self.name = ""
384

    
385
  def GET(self):
386
    """Returns a list of tags.
387

388
    Example: ["tag1", "tag2", "tag3"]
389

390
    """
391
    return baserlib._Tags_GET(self.TAG_LEVEL, name=self.name)
392

    
393
  def PUT(self):
394
    """Add a set of tags.
395

396
    The request as a list of strings should be PUT to this URI. And
397
    you'll have back a job id.
398

399
    """
400
    if 'tag' not in self.queryargs:
401
      raise http.HttpBadRequest("Please specify tag(s) to add using the"
402
                                " the 'tag' parameter")
403
    return baserlib._Tags_PUT(self.TAG_LEVEL,
404
                              self.queryargs['tag'], name=self.name,
405
                              dry_run=bool(self.dryRun()))
406

    
407
  def DELETE(self):
408
    """Delete a tag.
409

410
    In order to delete a set of tags, the DELETE
411
    request should be addressed to URI like:
412
    /tags?tag=[tag]&tag=[tag]
413

414
    """
415
    if 'tag' not in self.queryargs:
416
      # no we not gonna delete all tags
417
      raise http.HttpBadRequest("Cannot delete all tags - please specify"
418
                                " tag(s) using the 'tag' parameter")
419
    return baserlib._Tags_DELETE(self.TAG_LEVEL,
420
                                 self.queryargs['tag'],
421
                                 name=self.name,
422
                                 dry_run=bool(self.dryRun()))
423

    
424

    
425
class R_2_instances_name_tags(_R_Tags):
426
  """ /2/instances/[instance_name]/tags resource.
427

428
  Manages per-instance tags.
429

430
  """
431
  TAG_LEVEL = constants.TAG_INSTANCE
432

    
433

    
434
class R_2_nodes_name_tags(_R_Tags):
435
  """ /2/nodes/[node_name]/tags resource.
436

437
  Manages per-node tags.
438

439
  """
440
  TAG_LEVEL = constants.TAG_NODE
441

    
442

    
443
class R_2_tags(_R_Tags):
444
  """ /2/instances/tags resource.
445

446
  Manages cluster tags.
447

448
  """
449
  TAG_LEVEL = constants.TAG_CLUSTER