Commit c6b36078 authored by Christos Stavrakakis's avatar Christos Stavrakakis

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
# without modification, are permitted provided that the following
......@@ -31,67 +31,28 @@
# interpreted as representing official policies, either expressed
# 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):
help = "List flavors"
FIELDS = {
"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 + (
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)
fields = ["id", "name", "cpu", "ram", "disk", "template", "vms"]
# 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
# without modification, are permitted provided that the following
......@@ -33,31 +33,15 @@
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from synnefo.management.common import (format_bool, filter_results, UserCache,
Omit)
from synnefo.webproject.management.commands import ListCommand
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):
help = "List networks"
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"),
class Command(ListCommand):
option_list = ListCommand.option_list + (
make_option(
'--public',
action='store_true',
......@@ -65,104 +49,51 @@ class Command(BaseCommand):
default=False,
help="List only public networks"),
make_option(
'--user',
dest='user',
help="List only networks of the specified user"
" (uuid or display name"),
make_option('--ipv6',
'--ipv6',
action='store_true',
dest='ipv6',
default=False,
help="Show IPv6 information of the network"),
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"),
help="Include IPv6 information"),
)
def handle(self, *args, **options):
if args:
raise CommandError("Command doesn't accept any arguments")
ucache = UserCache()
if options['deleted']:
networks = Network.objects.all()
else:
networks = Network.objects.filter(deleted=False)
if options['public']:
networks = networks.filter(public=True)
user = options['user']
if user:
if '@' in user:
user = ucache.get_uuid(user)
networks = networks.filter(userid=user)
filter_by = options['filter_by']
if filter_by:
networks = filter_results(networks, filter_by)
displayname = options['displayname']
headers = filter(lambda x: x is not Omit,
['id',
'name',
'flavor',
'owner_uuid',
'owner_name' if displayname else Omit,
'mac_prefix',
'dhcp',
'state',
'link',
'vms',
'public',
])
if options['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)
object_class = Network
deleted_field = "deleted"
user_uuid_field = "userid"
def get_machines(network):
return network.machines.filter(deleted=False).count()
def get_backends(network):
return network.backend_networks.values_list("backend_id", flat=True)
FIELDS = {
"id": ("id", "The ID of the network"),
"name": ("name", "The name of the network"),
"user.uuid": ("userid", "The UUID of the network's owner"),
"public": ("public", "Whether network is public or private"),
"flavor": ("flavor", "The network's flavor"),
"state": ("state", "The network's state"),
"dhcp": ("dhcp", "Whether network uses nfdhcpd or not"),
"subnet.ipv4": ("subnet", "The IPv4 subnet of the network"),
"gateway.ipv4": ("gateway", "The IPv4 gateway of the network"),
"subnet.ipv6": ("subnet", "The IPv6 subnet of the network"),
"gateway.ipv6": ("gateway", "The IPv6 gateway of the network"),
"created": ("created", "The date the network was created"),
"updated": ("created", "The date the network was updated"),
"deleted": ("deleted", "Whether the network is deleted or not"),
"mode": ("mode", "The mode of the network"),
"link": ("link", "The link of the network"),
"mac_prefix": ("mac_prefix", "The network's MAC prefix"),
"vms": (get_machines, "Number of connected servers"),
"backends": (get_backends, "IDs of Ganeti backends that the network is"
" connected to"),
}
fields = ["id", "name", "user.uuid", "state", "public", "subnet.ipv4",
"gateway.ipv4", "link", "mac_prefix"]
def handle_args(self, *args, **options):
if options["public"]:
self.filters["public"] = True
if options["ipv6"]:
self.fields.extend(["subnet.ipv6", "gateway.ipv6"])
# 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
# without modification, are permitted provided that the following
......@@ -33,152 +33,96 @@
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
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.webproject.management.commands import ListCommand
from synnefo.db.models import VirtualMachine
from synnefo.management.common import get_backend
from synnefo.api.util import get_image
import logging
log = logging.getLogger(__name__)
FIELDS = VirtualMachine._meta.get_all_field_names()
from logging import getLogger
log = getLogger(__name__)
class Command(BaseCommand):
class Command(ListCommand):
help = "List servers"
option_list = BaseCommand.option_list + (
make_option(
'-c',
action='store_true',
dest='csv',
default=False,
help="Use pipes to separate values"),
option_list = ListCommand.option_list + (
make_option(
'--suspended',
action='store_true',
dest='suspended',
default=False,
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(
'--backend-id',
dest='backend_id',
help="List only servers of the specified backend"),
make_option(
'--user',
dest='user',
help="List only servers of the specified user (uuid or email)"),
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 \"operstate=STARTED,id>=22\"."
" Available keys are: %s" % ", ".join(FIELDS)),
"--build",
action="store_true",
dest="build",
default=False,
help="List only servers in the building state"),
make_option(
'--displayname',
action='store_true',
dest='displayname',
"--image-name",
action="store_true",
dest="image_name",
default=False,
help="Display both uuid and display name"),
help="Display image name instead of image ID"),
)
def handle(self, *args, **options):
if args:
raise CommandError("Command doesn't accept any arguments")
object_class = VirtualMachine
deleted_field = "deleted"
user_uuid_field = "userid"
ucache = UserCache()
if options['backend_id']:
backend = get_backend(options['backend_id'])
servers = backend.virtual_machines
else:
servers = VirtualMachine.objects
def get_public_ip(vm):
try:
return vm.nics.all()[0].ipv4
except IndexError:
return None
if options['deleted']:
servers = servers.all()
def format_vm_state(vm):
if vm.operstate == "BUILD":
return "BUILD(" + str(vm.buildpercentage) + "%)"
else:
servers = servers.filter(deleted=False)
if options['suspended']:
servers = servers.filter(suspended=True)
if options['build']:
servers = servers.filter(operstate='BUILD')
user = options['user']
if user:
if '@' in user:
user = ucache.get_uuid(user)
servers = servers.filter(userid=user)
filter_by = options['filter_by']
if filter_by:
servers = filter_results(servers, filter_by)
displayname = options['displayname']
cache = ImageCache()
headers = filter(lambda x: x is not Omit,
['id',
'name',
'owner_uuid',
'owner_name' if displayname else Omit,
'flavor',
'image',
'state',
'backend',
])
if displayname:
uuids = list(set([server.userid for server in servers]))
ucache.fetch_names(uuids)
table = []
for server in servers.order_by('id'):
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)
return vm.operstate
FIELDS = {
"id": ("id", "ID of the server"),
"name": ("name", "Name of the server"),
"user.uuid": ("userid", "The UUID of the server's owner"),
"flavor": ("flavor.name", "The name of the server's flavor"),
"backend": ("backend", "The Ganeti backend that hosts the VM"),
"image.id": ("imageid", "The ID of the server's image"),
"image.name": ("image", "The name of the server's image"),
"state": (format_vm_state, "The current state of the server"),
"ip": (get_public_ip, "The public IP of the server"),
"created": ("created", "The date the server was created"),
"deleted": ("deleted", "Whether the server is deleted or not"),
"suspended": ("suspended", "Whether the server is administratively"
" suspended"),
}
fields = ["id", "name", "user.uuid", "state", "flavor", "image.id",
"backend"]
def handle_args(self, *args, **options):
if options["suspended"]:
self.filters["suspended"] = True
if options["backend_id"]:
backend = get_backend(options["backend_id"])
self.filters["backend"] = backend.id
if options["build"]:
self.filters["operstate"] = "BUILD"
if options["image_name"]:
self.fields.replace("image.id", "image.name")
def handle_db_objects(self, rows, *args, **kwargs):
icache = ImageCache()
for vm in rows:
vm.image = icache.get_image(vm.imageid, vm.userid)
class ImageCache(object):
......
......@@ -31,40 +31,50 @@
# 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 pprint_table
from synnefo.db.models import Backend, IPPoolTable
from synnefo.webproject.management.commands import ListCommand
from synnefo.db.models import Backend
class Command(ListCommand):
help = "List Ganeti backends"
object_class = Backend
class Command(BaseCommand):
help = "List backends"
def get_vms(backend):
return backend.virtual_machines.filter(deleted=False).count()
option_list = BaseCommand.option_list + (
make_option('-c',
action='store_true',
dest='csv',
default=False,
help="Use pipes to separate values"),
)
def get_mem(backend):
return "%s/%s" % (backend.mfree, backend.mtotal)
def handle(self, *args, **options):
if args:
raise CommandError("Command doesn't accept any arguments")
def get_disk(backend):
return "%s/%s" % (backend.dfree, backend.dtotal)
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,
network__deleted=False):
network = bnet.network
try:
pool = IPPoolTable.objects.get(id=network.pool_id).pool
free_ips += pool.count_available()
total_ips += pool.pool_size
except IPPoolTable.DoesNotExist:
pass
return "%s/%s" % (free_ips, total_ips)
headers = ('id', 'clustername', 'port', 'username', "VMs", 'drained',
'offline')
table = []
for backend in backends:
id = str(backend.id)
vms = str(backend.virtual_machines.filter(deleted=False).count())
fields = (id, backend.clustername, str(backend.port),
backend.username, vms, str(backend.drained),
str(backend.offline))
table.append(fields)
FIELDS = {
"id": ("id", "Backend's unique ID"),
"clustername": ("clustername", "The name of the Ganeti cluster"),
"port": ("port", ""),
"username": ("username", "The RAPI user"),
"drained": ("drained", "Whether backend is marked as drained"),
"offline": ("offline", "Whether backend if marked as offline"),
"vms": (get_vms, "Number of VMs that this backend hosts"),
"ips": (get_ips, "free/total number of public IPs"),
"mem": (get_mem, "free/total memory (MB)"),
"disk": (get_mem, "free/total disk (GB)"),
}
separator =