Wednesday, January 7, 2009

Inside Query.get_machines()

In ECP 2.2, there exists a new Query class that defines a few class methods for querying the ECP database. The rational behind creating this class was that there are several queries that are very similar but require slightly varying degrees of flexibility. Also, it makes logical sense to group query methods into a class called Query. We know that using this class will in no way affect the state of the database.

The most pressing need for this class are machine queries. For example, there is often a need to select machines based on a specific cluster. Also, we may need to order these results and combine the various filters on a per-query basis.

Here is what the method definition looks like:
#ECP - The Query.get_machines() method.

@classmethod
def get_machines(cls, *args, **kw):
"""Retrieve a list of machines.
@status: Experimental"""
filter=False
join=[]
if kw.has_key('order_by'):
order_by=kw['order_by']
else:
order_by='machine_name'
if kw.has_key('machine_uuid'):
machine_uuid=kw['machine_uuid']
else:
machine_uuid=False
if kw.has_key('cluster_uuid'):
cluster_uuid=kw['cluster_uuid']
else:
cluster_uuid=False
if kw.has_key('namefilter'):
namefilter=kw['namefilter']
else:
namefilter=False
if kw.has_key('children'):
children=kw['children']
else:
children=False
if cluster_uuid:
try:
cluster_obj=UUIDSearch.cluster(cluster_uuid)
except E2ClusterNotFound, e:
e.store_traceback()
return []
filter=[]
machine_part=Machine.q.id==ClusterMachine.q.machine
join_part=ClusterMachine.q.clusters==cluster_obj.id
filter.append(machine_part)
filter.append(join_part)
if namefilter:
name_part=LIKE(Machine.q.machine_name, "%s%%"%(namefilter))
filter.append(name_part)
elif machine_uuid:
try:
machine_obj=UUIDSearch.machine(machine_uuid)
except E2MachineNotFound, e:
e.store_traceback()
return []
if children:
filter=[]
machine_part=Machine.q.parent==Hypervisor.q.id
join_part=Hypervisor.q.machine==machine_obj
filter.append(machine_part)
filter.append(join_part)
else:
return [machine_obj]
elif namefilter:
filter=LIKE(Machine.q.machine_name, "%s%%"%(namefilter))
result=PermSelect.machine(perm='r',\
orderby=order_by,\
filter=filter,\
join=join)
return result
The first to variables in the method, join and filter, are initialized first. They are always going to be sent to the final PermSelect.machine() invocation. Next, the keyword parameters are initialized. Here, the default values are assigned to the parameters if they are not provided.

If a cluster_uuid was specified, we retrieve the Cluster instance. We then construct the table-joining query component that joins the machine and cluster tables. If there is a name_filter specified, we construct a query component based on it.

If a machine_uuid was specified, we retrieve the machine instance. Then, we check if the children parameter was set to true. If so, we build the joining query components to select the children of the specified machine. Otherwise, we return the single machine instance.

Finally, we use the PermSelect class to execute the final query, which integrates permission checking in the selection. Another benefit of this method is that it will significantly cut down on the number of queries executed eventually.