Commit 6e99c5a0 authored by Iustin Pop's avatar Iustin Pop
Browse files

RAPI: fixes related to write mode

This patch fixes many small issues related to write functions:
  - update documentations w.r.t. how to add users
  - update the instance add function for latest API
  - add instance delete
  - fix addition of tags
  - update some error messages

Reviewed-by: imsnah
parent bfc30ec0
......@@ -106,7 +106,8 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
try:
ctx.handler_fn = getattr(ctx.handler, method)
except AttributeError, err:
raise http.HttpBadRequest()
raise http.HttpBadRequest("Method %s is unsupported for path %s" %
(method, req.request_path))
ctx.handler_access = getattr(ctx.handler, "%s_ACCESS" % method, None)
......
......@@ -188,10 +188,27 @@ class R_Generic(object):
try:
val = int(val)
except (ValueError, TypeError), err:
raise http.HttpBadRequest(message="Invalid value for the"
raise http.HttpBadRequest("Invalid value for the"
" '%s' parameter" % (name,))
return val
def getBodyParameter(self, name, *args):
"""Check and return the value for a given parameter.
If a second parameter is not given, an error will be returned,
otherwise this parameter specifies the default value.
@param name: the required parameter
"""
if name in self.req.request_body:
return self.req.request_body[name]
elif args:
return args[0]
else:
raise http.HttpBadRequest("Required parameter '%s' is missing" %
name)
def useLocking(self):
"""Check if the request specifies locking.
......
......@@ -324,32 +324,48 @@ class R_2_instances(baserlib.R_Generic):
@returns: a job id
"""
opts = self.req.request_post_data
beparams = baserlib.MakeParamsDict(opts, constants.BES_PARAMETERS)
hvparams = baserlib.MakeParamsDict(opts, constants.HVS_PARAMETERS)
if not isinstance(self.req.request_body, dict):
raise http.HttpBadRequest("Invalid body contents, not a dictionary")
beparams = baserlib.MakeParamsDict(self.req.request_body,
constants.BES_PARAMETERS)
hvparams = baserlib.MakeParamsDict(self.req.request_body,
constants.HVS_PARAMETERS)
fn = self.getBodyParameter
# disk processing
disk_data = fn('disks')
if not isinstance(disk_data, list):
raise http.HttpBadRequest("The 'disks' parameter should be a list")
disks = []
for idx, d in enumerate(disk_data):
if not isinstance(d, int):
raise http.HttpBadRequest("Disk %d specification wrong: should"
" be an integer")
disks.append({"size": d})
# nic processing (one nic only)
nics = [{"mac": fn("mac", constants.VALUE_AUTO),
"ip": fn("ip", None),
"bridge": fn("bridge", None)}]
op = ganeti.opcodes.OpCreateInstance(
instance_name=opts.get('name'),
disk_size=opts.get('size', 20 * 1024),
swap_size=opts.get('swap', 4 * 1024),
disk_template=opts.get('disk_template', None),
mode=constants.INSTANCE_CREATE,
os_type=opts.get('os'),
pnode=opts.get('pnode'),
snode=opts.get('snode'),
ip=opts.get('ip', 'none'),
bridge=opts.get('bridge', None),
start=opts.get('start', True),
ip_check=opts.get('ip_check', True),
wait_for_sync=opts.get('wait_for_sync', True),
mac=opts.get('mac', 'auto'),
hypervisor=opts.get('hypervisor', None),
instance_name=fn('name'),
disks=disks,
disk_template=fn('disk_template'),
os_type=fn('os'),
pnode=fn('pnode', None),
snode=fn('snode', None),
iallocator=fn('iallocator', None),
nics=nics,
start=fn('start', True),
ip_check=fn('ip_check', True),
wait_for_sync=True,
hypervisor=fn('hypervisor', None),
hvparams=hvparams,
beparams=beparams,
iallocator=opts.get('iallocator', None),
file_storage_dir=opts.get('file_storage_dir', None),
file_driver=opts.get('file_driver', 'loop'),
file_storage_dir=fn('file_storage_dir', None),
file_driver=fn('file_driver', 'loop'),
)
job_id = ganeti.cli.SendJob([op])
......@@ -373,6 +389,15 @@ class R_2_instances_name(baserlib.R_Generic):
return baserlib.MapFields(I_FIELDS, result[0])
def DELETE(self):
"""Delete an instance.
"""
op = ganeti.opcodes.OpRemoveInstance(instance_name=self.items[0],
ignore_failures=False)
job_id = ganeti.cli.SendJob([op])
return job_id
class R_2_instances_name_reboot(baserlib.R_Generic):
"""/2/instances/[instance_name]/reboot resource.
......@@ -489,8 +514,11 @@ class _R_Tags(baserlib.R_Generic):
you'll have back a job id.
"""
if 'tag' not in self.queryargs:
raise http.HttpBadRequest("Please specify tag(s) to add using the"
" the 'tag' parameter")
return baserlib._Tags_PUT(self.TAG_LEVEL,
self.req.request_post_data, name=self.name)
self.queryargs['tag'], name=self.name)
def DELETE(self):
"""Delete a tag.
......@@ -502,7 +530,8 @@ class _R_Tags(baserlib.R_Generic):
"""
if 'tag' not in self.queryargs:
# no we not gonna delete all tags
raise http.HttpNotImplemented()
raise http.HttpBadRequest("Cannot delete all tags - please specify"
" tag(s) using the 'tag' parameter")
return baserlib._Tags_DELETE(self.TAG_LEVEL,
self.queryargs['tag'],
name=self.name)
......
......@@ -47,6 +47,7 @@
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>DESCRIPTION</title>
......@@ -81,6 +82,38 @@
</refsect1>
<refsect1>
<title>ACCESS CONTROLS</title>
<para>
All query operations are allowed without authentication. Only
the modification operations require authentication, in the form
of basic authentication.
</para>
<para>
The users and their rights are defined in a file named
<filename>rapi_users</filename>, located in the <filename
class="directory">@LOCALSTATEDIR@/ganeti</filename>
directory. The users should be listed one per line, in the
following format:
</para>
<screen>username password options</screen>
<para>
Currently the <replaceable>options</replaceable> field should
equal the string <emphasis>write</emphasis> in order to actually
give write permission for the given users. Example:
</para>
<screen>rclient secret write
guest tespw
</screen>
<para>The first user (<userinput>rclient</userinput>) will have
read-write rights, whereas the second user does only have read
(query) rights, and as such is no different than not using
authentication at all.</para>
</refsect1>
&footer;
</refentry>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment