Commit 82476a1e authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

cyclades: Add management commands for volumes

parent 9571d0ea
......@@ -479,16 +479,21 @@ def nics_from_instance(i):
return nics
def disks_from_instance(i):
sizes = zip(itertools.repeat('size'), i['disk.sizes'])
names = zip(itertools.repeat('name'), i['disk.names'])
uuids = zip(itertools.repeat('uuid'), i['disk.uuids'])
disks = zip(sizes, names, uuids)
disks = map(lambda x: dict(x), disks)
#disks = dict(enumerate(disks))
return disks
def get_ganeti_jobs(backend):
gnt_jobs = backend_mod.get_jobs(backend)
return dict([(int(j["id"]), j) for j in gnt_jobs])
def disks_from_instance(i):
return dict([(index, {"size": size})
for index, size in enumerate(i["disk.sizes"])])
class NetworkReconciler(object):
def __init__(self, logger, fix=False):
self.log = logger
......
......@@ -35,7 +35,7 @@ from django.core.management import CommandError
from synnefo.db.models import (Backend, VirtualMachine, Network,
Flavor, IPAddress, Subnet,
BridgePoolTable, MacPrefixPoolTable,
NetworkInterface)
NetworkInterface, Volume)
from functools import wraps
from django.conf import settings
......@@ -61,6 +61,7 @@ RESOURCE_MAP = {
"backend": Backend.objects,
"flavor": Flavor.objects,
"server": VirtualMachine.objects,
"volume": Volume.objects,
"network": Network.objects,
"subnet": Subnet.objects,
"port": NetworkInterface.objects,
......
......@@ -41,7 +41,8 @@ from synnefo.db.models import Backend, pooled_rapi_client
from synnefo.db.pools import bitarray_to_map
from synnefo.logic.rapi import GanetiApiError
from synnefo.logic.reconciliation import nics_from_instance
from synnefo.logic.reconciliation import (nics_from_instance,
disks_from_instance)
from synnefo.management.common import get_image
......@@ -259,16 +260,16 @@ def pprint_port_in_ganeti(port, stdout=None, title=None):
return
raise e
nics = nics_from_instance(vm_info)
disks = disks_from_instance(vm_info)
try:
gnt_nic = filter(lambda nic: nic.get("name") == port.backend_uuid,
nics)[0]
gnt_nic["instance"] = vm_info["name"]
gnt_disk = filter(lambda disk: disk.get("name") == port.backend_uuid,
disks)[0]
gnt_disk["instance"] = vm_info["name"]
except IndexError:
stdout.write("Port %s is not attached to instance %s\n" %
(port.id, vm.id))
return
pprint_table(stdout, gnt_nic.items(), None, separator=" | ",
pprint_table(stdout, gnt_disk.items(), None, separator=" | ",
title=title)
vm.put_client(client)
......@@ -382,3 +383,68 @@ def pprint_server_in_ganeti(server, print_jobs=False, stdout=None, title=None):
separator=" | ",
title="Ganeti Job %s" % server_job["id"])
server.put_client(client)
def pprint_volume(volume, display_mails=False, stdout=None, title=None):
if stdout is None:
stdout = sys.stdout
if title is None:
title = "State of volume %s in DB" % volume.id
ucache = UserCache(ASTAKOS_AUTH_URL, ASTAKOS_TOKEN)
userid = volume.userid
volume_dict = OrderedDict([
("id", volume.id),
("size", volume.size),
("disk_template", volume.disk_template),
("disk_provider", volume.disk_provider),
("server_id", volume.machine_id),
("userid", volume.userid),
("username", ucache.get_name(userid) if display_mails else None),
("name", volume.name),
("state", volume.status),
("deleted", volume.deleted),
("backendjobid", volume.backendjobid),
])
pprint_table(stdout, volume_dict.items(), None, separator=" | ",
title=title)
def pprint_volume_in_ganeti(volume, stdout=None, title=None):
if stdout is None:
stdout = sys.stdout
if title is None:
title = "State of volume %s in Ganeti" % volume.id
vm = volume.machine
if vm is None:
stdout.write("volume is not attached to any instance.\n")
return
client = vm.get_client()
try:
vm_info = client.GetInstance(vm.backend_vm_id)
except GanetiApiError as e:
if e.code == 404:
stdout.write("Volume seems attached to server %s, but"
" server does not exist in backend.\n"
% vm)
return
raise e
disks = disks_from_instance(vm_info)
try:
gnt_disk = filter(lambda disk:
disk.get("name") == volume.backend_volume_uuid,
disks)[0]
gnt_disk["instance"] = vm_info["name"]
except IndexError:
stdout.write("Volume %s is not attached to instance %s\n" % (volume.id,
vm.id))
return
pprint_table(stdout, gnt_disk.items(), None, separator=" | ",
title=title)
vm.put_client(client)
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from synnefo.management import common
#from snf_django.management.utils import parse_bool
from synnefo.volume import snapshots
class Command(BaseCommand):
args = "<volume ID>"
help = "Create a snapshot from the specified volume"
option_list = BaseCommand.option_list + (
make_option(
'--wait',
dest='wait',
default="True",
choices=["True", "False"],
metavar="True|False",
help="Wait for Ganeti job to complete."),
make_option(
"--name",
dest="name",
default=None,
help="Display name of the snapshot"),
make_option(
"--description",
dest="description",
default=None,
help="Display description of the snapshot"),
)
@common.convert_api_faults
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError("Please provide a volume ID")
volume = common.get_volume(args[0])
name = options.get("name")
if name is None:
raise CommandError("'name' option is required")
description = options.get("description")
if description is None:
description = "Snapshot of Volume '%s" % volume.id
snapshot = snapshots.create(volume.userid,
volume,
name=name,
description=description,
metadata={})
msg = ("Created snapshot of volume '%s' with ID %s\n"
% (volume.id, snapshot["uuid"]))
self.stdout.write(msg)
# Copyright 2011-2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of GRNET S.A.
#
from django.core.management.base import BaseCommand
from optparse import make_option
from snf_django.management.utils import pprint_table
from synnefo.plankton.utils import image_backend
class Command(BaseCommand):
help = "List public snapshots or snapshots available to a user."
option_list = BaseCommand.option_list + (
make_option(
'--user-id',
dest='userid',
default=None,
help="List all snapshots available to that user."
" If no user is specified, only public snapshots"
" are displayed."),
)
def handle(self, **options):
user = options['userid']
with image_backend(user) as backend:
snapshots = backend.list_snapshots(user)
headers = ("id", "name", "volume_id", "size", "map")
table = []
for snap in snapshots:
fields = (snap["uuid"], snap["name"], snap["volume_id"],
snap["size"], snap["map"])
table.append(fields)
pprint_table(self.stdout, table, headers)
# Copyright 2011-2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of GRNET S.A.
#
from optparse import make_option
from django.core.management.base import CommandError
from synnefo.volume import snapshots, util
from synnefo.management import common
from snf_django.management.commands import RemoveCommand
class Command(RemoveCommand):
args = "<Snapshot ID> [<Snapshot ID> ...]"
help = "Remove a snapshot"
option_list = RemoveCommand.option_list + (
make_option(
"--user_id",
dest="user_id",
default=None,
help="UUID of the owner of the snapshot"),
)
@common.convert_api_faults
def handle(self, *args, **options):
if not args:
raise CommandError("Please provide a snapshot ID")
force = options['force']
message = "snapshots" if len(args) > 1 else "snapshot"
self.confirm_deletion(force, message, args)
user_id = self.options["user_id"]
for snapshot_id in args:
self.stdout.write("\n")
try:
snapshot = util.get_snapshot(user_id, snapshot_id)
snapshots.delete(snapshot)
self.stdout.write("Successfully removed snapshot %s\n"
% snapshot)
except CommandError as e:
self.stdout.write("Error -- %s\n" % e.message)
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from snf_django.management.utils import parse_bool
from synnefo.management import common, pprint
from synnefo.volume import volumes
HELP_MSG = """Create a new volume."""
class Command(BaseCommand):
help = HELP_MSG
option_list = BaseCommand.option_list + (
make_option(
"--name",
dest="name",
default=None,
help="Display name of the volume."),
make_option(
"--description",
dest="description",
default=None,
help="Display description of the volume."),
make_option(
"--owner",
dest="user_id",
default=None,
help="UUID of the owner of the volume."),
make_option(
"-s", "--size",
dest="size",
default=None,
help="Size of the new volume in GB"),
make_option(
"--server",
dest="server_id",
default=None,
help="The ID of the server that the volume will be connected to."),
make_option(
"--wait",
dest="wait",
default="True",
choices=["True", "False"],
metavar="True|False",
help="Wait for Ganeti jobs to complete."),
)
@common.convert_api_faults
def handle(self, *args, **options):
if args:
raise CommandError("Command doesn't accept any arguments")
size = options.get("size")
user_id = options.get("user_id")
server_id = options.get("server_id")
wait = parse_bool(options["wait"])
display_name = options.get("name", "")
display_description = options.get("description", "")
if size is None:
raise CommandError("Please specify the size of the volume")
if server_id is None:
raise CommandError("Please specify the server to attach the"
" volume.")
vm = common.get_resource("server", server_id, for_update=True)
if user_id is None:
user_id = vm.userid
source_image_id = source_volume_id = source_snapshot_id = None
volume = volumes.create(user_id, size, server_id,
name=display_name,
description=display_description,
source_image_id=source_image_id,
source_snapshot_id=source_snapshot_id,
source_volume_id=source_volume_id,
metadata={})
self.stdout.write("Created volume '%s' in DB:\n" % volume.id)
pprint.pprint_volume(volume, stdout=self.stdout)
self.stdout.write("\n")
if volume.machine is not None:
volume.machine.task_job_id = volume.backendjobid
common.wait_server_task(volume.machine, wait, stdout=self.stdout)
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from synnefo.management.common import convert_api_faults
from synnefo.management import pprint, common
class Command(BaseCommand):
help = "Inspect a Volume on DB and Ganeti"
args = "<volume ID>"
option_list = BaseCommand.option_list + (
make_option(
'--displayname',
action='store_true',
dest='displayname',
default=False,
help="Display both uuid and display name"),
)
@convert_api_faults
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError("Please provide a volume ID")
volume = common.get_volume("volume", args[0])
pprint.pprint_volume(volume, stdout=self.stdout)
self.stdout.write('\n\n')
pprint.pprint_volume_in_ganeti(volume, stdout=self.stdout)
# Copyright 2012-2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#