Commit c6b36078 authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

Add ListCommand to be used for all *-list commands

Create a generic ListCommand to be user for all *-list management
commands. ListCommand handles must tasks that are common to all
management commands, like retrieving objects from db, filtering
results and formating output to a pretty table, json or csv.

Also, implement server-list, network-list, backend-list and flavor-list
as subclasses of this command.
parent 7386cfc0
# Copyright 2012 GRNET S.A. All rights reserved. # Copyright 2012-2013 GRNET S.A. All rights reserved.
# #
# Redistribution and use in source and binary forms, with or # Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following # without modification, are permitted provided that the following
...@@ -31,67 +31,28 @@ ...@@ -31,67 +31,28 @@
# interpreted as representing official policies, either expressed # interpreted as representing official policies, either expressed
# or implied, of GRNET S.A. # or implied, of GRNET S.A.
from optparse import make_option from synnefo.webproject.management.commands import ListCommand
from synnefo.db.models import Flavor, VirtualMachine
from django.core.management.base import BaseCommand, CommandError
from synnefo.management.common import (format_bool, filter_results,
pprint_table)
from synnefo.db.models import Flavor class Command(ListCommand):
help = "List available server flavors"
FIELDS = Flavor._meta.get_all_field_names() object_class = Flavor
deleted_field = "deleted"
def get_vms(flavor):
return VirtualMachine.objects.filter(flavor=flavor, deleted=False)\
.count()
class Command(BaseCommand): FIELDS = {
help = "List flavors" "id": ("id", "Flavor's unique ID"),
"name": ("name", "Flavor's unique name"),
"cpu": ("cpu", "Number of CPUs"),
"ram": ("ram", "Size(MB) of RAM"),
"disk": ("disk", "Size(GB) of disk"),
"template": ("disk_template", "Disk template"),
"vms": (get_vms, "Number of active servers using this flavor")
}
option_list = BaseCommand.option_list + ( fields = ["id", "name", "cpu", "ram", "disk", "template", "vms"]
make_option(
'-c',
action='store_true',
dest='csv',
default=False,
help="Use pipes to separate values"),
make_option(
'--deleted',
action='store_true',
dest='deleted',
default=False,
help="Include deleted flavors"),
make_option(
'--filter-by',
dest='filter_by',
help="Filter results. Comma seperated list of key=val pairs"
" that displayed entries must satisfy. e.g."
" --filter-by \"cpu=1,ram!=1024\"."
"Available keys are: %s" % ", ".join(FIELDS))
)
def handle(self, *args, **options):
if args:
raise CommandError("Command doesn't accept any arguments")
if options['deleted']:
flavors = Flavor.objects.all()
else:
flavors = Flavor.objects.filter(deleted=False)
filter_by = options['filter_by']
if filter_by:
flavors = filter_results(flavors, filter_by)
headers = ('id', 'name', 'cpus', 'ram', 'disk', 'template', 'deleted')
table = []
for flavor in flavors.order_by('id'):
id = str(flavor.id)
cpu = str(flavor.cpu)
ram = str(flavor.ram)
disk = str(flavor.disk)
deleted = format_bool(flavor.deleted)
fields = (id, flavor.name, cpu, ram, disk, flavor.disk_template,
deleted)
table.append(fields)
separator = " | " if options['csv'] else None
pprint_table(self.stdout, table, headers, separator)
# Copyright 2012 GRNET S.A. All rights reserved. # Copyright 2012-2013 GRNET S.A. All rights reserved.
# #
# Redistribution and use in source and binary forms, with or # Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following # without modification, are permitted provided that the following
...@@ -33,31 +33,15 @@ ...@@ -33,31 +33,15 @@
from optparse import make_option from optparse import make_option
from django.core.management.base import BaseCommand, CommandError from synnefo.webproject.management.commands import ListCommand
from synnefo.management.common import (format_bool, filter_results, UserCache,
Omit)
from synnefo.db.models import Network from synnefo.db.models import Network
from synnefo.management.common import pprint_table
FIELDS = Network._meta.get_all_field_names() from logging import getLogger
log = getLogger(__name__)
class Command(BaseCommand): class Command(ListCommand):
help = "List networks" option_list = ListCommand.option_list + (
option_list = BaseCommand.option_list + (
make_option(
'-c',
action='store_true',
dest='csv',
default=False,
help="Use pipes to separate values"),
make_option(
'--deleted',
action='store_true',
dest='deleted',
default=False,
help="Include deleted networks"),
make_option( make_option(
'--public', '--public',
action='store_true', action='store_true',
...@@ -65,104 +49,51 @@ class Command(BaseCommand): ...@@ -65,104 +49,51 @@ class Command(BaseCommand):
default=False, default=False,
help="List only public networks"), help="List only public networks"),
make_option( make_option(
'--user', '--ipv6',
dest='user',
help="List only networks of the specified user"
" (uuid or display name"),
make_option('--ipv6',
action='store_true', action='store_true',
dest='ipv6', dest='ipv6',
default=False, default=False,
help="Show IPv6 information of the network"), help="Include IPv6 information"),
make_option(
'--filter-by',
dest='filter_by',
help="Filter results. Comma seperated list of key 'cond' val pairs"
" that displayed entries must satisfy. e.g."
" --filter-by \"name=Network-1,link!=prv0\"."
" Available keys are: %s" % ", ".join(FIELDS)),
make_option(
'--displayname',
action='store_true',
dest='displayname',
default=False,
help="Display both uuid and display name"),
) )
def handle(self, *args, **options): object_class = Network
if args: deleted_field = "deleted"
raise CommandError("Command doesn't accept any arguments") user_uuid_field = "userid"
ucache = UserCache() def get_machines(network):
return network.machines.filter(deleted=False).count()
if options['deleted']:
networks = Network.objects.all() def get_backends(network):
else: return network.backend_networks.values_list("backend_id", flat=True)
networks = Network.objects.filter(deleted=False)
FIELDS = {
if options['public']: "id": ("id", "The ID of the network"),
networks = networks.filter(public=True) "name": ("name", "The name of the network"),
"user.uuid": ("userid", "The UUID of the network's owner"),
user = options['user'] "public": ("public", "Whether network is public or private"),
if user: "flavor": ("flavor", "The network's flavor"),
if '@' in user: "state": ("state", "The network's state"),
user = ucache.get_uuid(user) "dhcp": ("dhcp", "Whether network uses nfdhcpd or not"),
networks = networks.filter(userid=user) "subnet.ipv4": ("subnet", "The IPv4 subnet of the network"),
"gateway.ipv4": ("gateway", "The IPv4 gateway of the network"),
filter_by = options['filter_by'] "subnet.ipv6": ("subnet", "The IPv6 subnet of the network"),
if filter_by: "gateway.ipv6": ("gateway", "The IPv6 gateway of the network"),
networks = filter_results(networks, filter_by) "created": ("created", "The date the network was created"),
"updated": ("created", "The date the network was updated"),
displayname = options['displayname'] "deleted": ("deleted", "Whether the network is deleted or not"),
"mode": ("mode", "The mode of the network"),
headers = filter(lambda x: x is not Omit, "link": ("link", "The link of the network"),
['id', "mac_prefix": ("mac_prefix", "The network's MAC prefix"),
'name', "vms": (get_machines, "Number of connected servers"),
'flavor', "backends": (get_backends, "IDs of Ganeti backends that the network is"
'owner_uuid', " connected to"),
'owner_name' if displayname else Omit, }
'mac_prefix',
'dhcp', fields = ["id", "name", "user.uuid", "state", "public", "subnet.ipv4",
'state', "gateway.ipv4", "link", "mac_prefix"]
'link',
'vms', def handle_args(self, *args, **options):
'public', if options["public"]:
]) self.filters["public"] = True
if options["ipv6"]:
if options['ipv6']: self.fields.extend(["subnet.ipv6", "gateway.ipv6"])
headers.extend(['IPv6 Subnet', 'IPv6 Gateway'])
else:
headers.extend(['IPv4 Subnet', 'IPv4 Gateway'])
if displayname:
uuids = list(set([network.userid for network in networks]))
ucache.fetch_names(uuids)
table = []
for network in networks.order_by("id"):
uuid = network.userid
if displayname:
dname = ucache.get_name(uuid)
fields = filter(lambda x: x is not Omit,
[str(network.id),
network.name,
network.flavor,
uuid or '-',
dname or '-' if displayname else Omit,
network.mac_prefix or '-',
str(network.dhcp),
network.state,
network.link or '-',
str(network.machines.count()),
format_bool(network.public),
])
if options['ipv6']:
fields.extend([network.subnet6 or '', network.gateway6 or ''])
else:
fields.extend([network.subnet, network.gateway or ''])
table.append(fields)
separator = " | " if options['csv'] else None
pprint_table(self.stdout, table, headers, separator)
# Copyright 2012 GRNET S.A. All rights reserved. # Copyright 2012-2013 GRNET S.A. All rights reserved.
# #
# Redistribution and use in source and binary forms, with or # Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following # without modification, are permitted provided that the following
...@@ -33,152 +33,96 @@ ...@@ -33,152 +33,96 @@
from optparse import make_option from optparse import make_option
from django.core.management.base import BaseCommand, CommandError from synnefo.webproject.management.commands import ListCommand
from synnefo.management.common import (format_vm_state, get_backend, Omit,
filter_results, pprint_table, UserCache)
from synnefo.api.util import get_image
from synnefo.db.models import VirtualMachine from synnefo.db.models import VirtualMachine
from synnefo.management.common import get_backend
from synnefo.api.util import get_image
import logging from logging import getLogger
log = logging.getLogger(__name__) log = getLogger(__name__)
FIELDS = VirtualMachine._meta.get_all_field_names()
class Command(BaseCommand): class Command(ListCommand):
help = "List servers" help = "List servers"
option_list = BaseCommand.option_list + ( option_list = ListCommand.option_list + (
make_option(
'-c',
action='store_true',
dest='csv',
default=False,
help="Use pipes to separate values"),
make_option( make_option(
'--suspended', '--suspended',
action='store_true', action='store_true',
dest='suspended', dest='suspended',
default=False, default=False,
help="List only suspended servers"), help="List only suspended servers"),
make_option(
'--build',
action='store_true',
dest='build',
default=False,
help="List only servers in the building state"),
make_option(
'--deleted',
action='store_true',
dest='deleted',
default=False,
help="Include deleted servers"),
make_option( make_option(
'--backend-id', '--backend-id',
dest='backend_id', dest='backend_id',
help="List only servers of the specified backend"), help="List only servers of the specified backend"),
make_option( make_option(
'--user', "--build",
dest='user', action="store_true",
help="List only servers of the specified user (uuid or email)"), dest="build",
make_option( default=False,
'--filter-by', help="List only servers in the building state"),
dest='filter_by',
help="Filter results. Comma seperated list of key `cond` val pairs"
" that displayed entries must satisfy. e.g."
" --filter-by \"operstate=STARTED,id>=22\"."
" Available keys are: %s" % ", ".join(FIELDS)),
make_option( make_option(
'--displayname', "--image-name",
action='store_true', action="store_true",
dest='displayname', dest="image_name",
default=False, default=False,
help="Display both uuid and display name"), help="Display image name instead of image ID"),
) )
def handle(self, *args, **options): object_class = VirtualMachine
if args: deleted_field = "deleted"
raise CommandError("Command doesn't accept any arguments") user_uuid_field = "userid"
ucache = UserCache() def get_public_ip(vm):
try:
if options['backend_id']: return vm.nics.all()[0].ipv4
backend = get_backend(options['backend_id']) except IndexError:
servers = backend.virtual_machines return None
else:
servers = VirtualMachine.objects
if options['deleted']: def format_vm_state(vm):
servers = servers.all() if vm.operstate == "BUILD":
return "BUILD(" + str(vm.buildpercentage) + "%)"
else: else:
servers = servers.filter(deleted=False) return vm.operstate
if options['suspended']: FIELDS = {
servers = servers.filter(suspended=True) "id": ("id", "ID of the server"),
"name": ("name", "Name of the server"),
if options['build']: "user.uuid": ("userid", "The UUID of the server's owner"),
servers = servers.filter(operstate='BUILD') "flavor": ("flavor.name", "The name of the server's flavor"),
"backend": ("backend", "The Ganeti backend that hosts the VM"),
user = options['user'] "image.id": ("imageid", "The ID of the server's image"),
if user: "image.name": ("image", "The name of the server's image"),
if '@' in user: "state": (format_vm_state, "The current state of the server"),
user = ucache.get_uuid(user) "ip": (get_public_ip, "The public IP of the server"),
servers = servers.filter(userid=user) "created": ("created", "The date the server was created"),
"deleted": ("deleted", "Whether the server is deleted or not"),
filter_by = options['filter_by'] "suspended": ("suspended", "Whether the server is administratively"
if filter_by: " suspended"),
servers = filter_results(servers, filter_by) }
displayname = options['displayname'] fields = ["id", "name", "user.uuid", "state", "flavor", "image.id",
"backend"]
cache = ImageCache()
def handle_args(self, *args, **options):
headers = filter(lambda x: x is not Omit, if options["suspended"]:
['id', self.filters["suspended"] = True
'name',
'owner_uuid', if options["backend_id"]:
'owner_name' if displayname else Omit, backend = get_backend(options["backend_id"])
'flavor', self.filters["backend"] = backend.id
'image',
'state', if options["build"]:
'backend', self.filters["operstate"] = "BUILD"
])
if options["image_name"]:
if displayname: self.fields.replace("image.id", "image.name")
uuids = list(set([server.userid for server in servers]))
ucache.fetch_names(uuids) def handle_db_objects(self, rows, *args, **kwargs):
icache = ImageCache()
table = [] for vm in rows:
for server in servers.order_by('id'): vm.image = icache.get_image(vm.imageid, vm.userid)
try:
name = server.name.decode('utf8')
except UnicodeEncodeError:
name = server.name
flavor = server.flavor.name
image = cache.get_image(server.imageid, server.userid)
state = format_vm_state(server)
uuid = server.userid
if displayname:
dname = ucache.get_name(server.userid)
fields = filter(lambda x: x is not Omit,
[str(server.id),
name,
uuid,
dname if displayname else Omit,
flavor,
image,
state,
str(server.backend),
])
table.append(fields)
separator = " | " if options['csv'] else None
pprint_table(self.stdout, table, headers, separator)
class ImageCache(object): class ImageCache(object):
......
...@@ -31,40 +31,50 @@ ...@@ -31,40 +31,50 @@
# interpreted as representing official policies, either expressed # interpreted as representing official policies, either expressed
# or implied, of GRNET S.A. # or implied, of GRNET S.A.
from optparse import make_option from synnefo.db.models import Backend, IPPoolTable
from django.core.management.base import BaseCommand, CommandError from synnefo.webproject.management.commands import ListCommand
from synnefo.management.common import pprint_table
from synnefo.db.models import Backend
class Command(ListCommand):
help = "List Ganeti backends"
object_class = Backend
class Command(BaseCommand): def get_vms(backend):
help = "List backends" return backend.virtual_machines.filter(deleted=False).count()
option_list = BaseCommand.option_list + ( def get_mem(backend):
make_option('-c', return "%s/%s" % (backend.mfree, backend.mtotal)
action='store_true',
dest='csv',
default=False,
help="Use pipes to separate values"),
)
def handle(self, *args, **options): def get_disk(backend):
if args: return "%s/%s" % (backend.dfree, backend.dtotal)
raise CommandError("Command doesn't accept any arguments")
backends = Backend.objects.order_by('id') def get_ips(backend):
free_ips = 0
total_ips = 0
for bnet in backend.networks.filter(deleted=False,
network__public=True,