commit b0e7f310601c01258c9a4ad27e6fe59c2b7a1742
Author: Christos Stavrakakis <cstavr@grnet.gr>
Date:   Thu Mar 14 11:44:56 2013 +0200

    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.

diff --git a/snf-cyclades-app/synnefo/api/management/commands/server-list.py b/snf-cyclades-app/synnefo/api/management/commands/server-list.py
index 467d4e6..9579039 100644
--- a/snf-cyclades-app/synnefo/api/management/commands/server-list.py
+++ b/snf-cyclades-app/synnefo/api/management/commands/server-list.py
@@ -1,4 +1,4 @@
-# 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,28 +33,19 @@
 
 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',
@@ -62,123 +53,76 @@ class Command(BaseCommand):
             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):
