Index: fedd/fedd_client.py
===================================================================
--- fedd/fedd_client.py	(revision d27fd76d6b9be5d68a54cd3f7c755d9f1e4f4738)
+++ fedd/fedd_client.py	(revision 65f3f29cb7ca0c59eb17efb5e2786c359903a4a7)
@@ -157,4 +157,12 @@
 		help="data to extract")
 
+class fedd_multi_exp_data_opts(fedd_client_opts):
+    def __init__(self):
+	fedd_client_opts.__init__(self)
+	self.add_option("--data", dest="data", default=[],
+		action="append", type="choice",
+		choices=("id", "federant", "vtopo", "vis", "log", "status"),
+		help="data to extract")
+
 class fedd_spew_opts(fedd_client_opts):
     def __init__(self):
@@ -353,13 +361,11 @@
 	    raise RuntimeError("No body in response??")
 
-# Querying experiment data follows the same control flow regardless of the
-# specific data retrieved.  This class encapsulates that control flow.
-class exp_data(fedd_rpc):
-    def __init__(self): 
+class exp_data_base(fedd_rpc):
+    def __init__(self, op='Info'):
 	"""
 	Init the various conversions
 	"""
 
-	fedd_rpc.__init__(self, 'Info')
+	fedd_rpc.__init__(self, op)
 	# List of things one could ask for and what formatting routine is
 	# called.
@@ -374,5 +380,5 @@
 	    }
 
-
+    # Utility functions
     def print_string(self, d, out=sys.stdout):
 	print >>out, d
@@ -403,4 +409,12 @@
 	    print >>out, str
 
+	
+
+# Querying experiment data follows the same control flow regardless of the
+# specific data retrieved.  This class encapsulates that control flow.
+class exp_data(exp_data_base):
+    def __init__(self): 
+	exp_data_base.__init__(self, 'Info')
+
     def __call__(self):
 	"""
@@ -495,4 +509,153 @@
 	sys.argv.append('--data=status')
 	exp_data.__call__(self)
+
+class multi_exp_data(exp_data_base):
+    def __init__(self): 
+	exp_data_base.__init__(self, 'MultiInfo')
+
+
+    def __call__(self):
+	"""
+	The control flow.  Compose the request and print the response.
+	"""
+	# Process the options using the customized option parser defined above
+	parser = fedd_multi_exp_data_opts()
+
+	(opts, args) = parser.parse_args()
+
+	if opts.trusted:
+	    if ( not os.access(opts.trusted, os.R_OK) ) :
+		sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
+
+	if opts.debug > 0: opts.tracefile=sys.stderr
+
+	(user, cert) = self.get_user_info([])
+
+	if opts.cert != None: cert = opts.cert
+
+	if cert == None:
+	    sys.exit("No certificate given (--cert) or found")
+
+	if os.access(cert, os.R_OK):
+	    fid = fedid(file=cert)
+	else:
+	    sys.exit("Cannot read certificate (%s)" % cert)
+
+	req = { }
+
+	try:
+	    resp_dict = self.do_rpc(req,
+		    opts.url, opts.transport, cert, opts.trusted, 
+		    serialize_only=opts.serialize_only,
+		    tracefile=opts.tracefile)
+	except self.RPCException, e:
+	    exit_with_fault(\
+		    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
+	except RuntimeError, e:
+	    print e
+	    sys.exit("Error processing RPC: %s" % e)
+
+	exps = resp_dict.get('info', [])
+	if exps:
+	    print '---'
+	    for exp in exps:
+		for d in opts.data:
+		    key, output = self.params[d]
+		    try:
+			if exp.has_key(key):
+			    output(exp[key])
+		    except RuntimeError, e:
+			sys.exit("Bad response. %s" % e.message)
+		print '---'
+
+
+class multi_status(exp_data_base):
+    def __init__(self): 
+	exp_data_base.__init__(self, 'MultiInfo')
+
+
+    def __call__(self):
+	"""
+	The control flow.  Compose the request and print the response.
+	"""
+	# Process the options using the customized option parser defined above
+	parser = fedd_client_opts()
+
+	(opts, args) = parser.parse_args()
+
+	if opts.trusted:
+	    if ( not os.access(opts.trusted, os.R_OK) ) :
+		sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
+
+	if opts.debug > 0: opts.tracefile=sys.stderr
+
+	(user, cert) = self.get_user_info([])
+
+	if opts.cert != None: cert = opts.cert
+
+	if cert == None:
+	    sys.exit("No certificate given (--cert) or found")
+
+	if os.access(cert, os.R_OK):
+	    fid = fedid(file=cert)
+	else:
+	    sys.exit("Cannot read certificate (%s)" % cert)
+
+	req = { }
+
+	try:
+	    resp_dict = self.do_rpc(req,
+		    opts.url, opts.transport, cert, opts.trusted, 
+		    serialize_only=opts.serialize_only,
+		    tracefile=opts.tracefile)
+	except self.RPCException, e:
+	    exit_with_fault(\
+		    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
+	except RuntimeError, e:
+	    print e
+	    sys.exit("Error processing RPC: %s" % e)
+
+	for exp in resp_dict.get('info', []):
+	    out = []
+	    for eid in exp.get('experimentID', []):
+		if eid.has_key('localname'):
+		    out.append(eid['localname'])
+		    break
+	    else:
+		out.append("")
+	    for eid in exp.get('experimentID', []):
+		if eid.has_key('fedid'):
+		    out.append("%s" % eid['fedid'])
+		    break
+	    else:
+		out.append("")
+
+	    out.append(exp.get('experimentStatus', ""))
+
+	    for f in exp.get('federant', []):
+		if f.get('master', False):
+		    em = f.get('emulab', None)
+		    if em:
+			project = em.get('project', None)
+			if project:
+			    tb = project.get('testbed', None)
+			    if tb and tb.has_key('localname'):
+				out.append(tb['localname'])
+			    else:
+				out.append("")
+			    pn = project.get('name', None)
+			    if pn and pn.has_key('localname'):
+				out.append(pn['localname'])
+			    else:
+				out.append("")
+			else:
+			    out.extend(("", ""))
+		    else:
+			out.extend(("", ""))
+		    break
+	    else:
+		out.extend(("",""))
+
+	    print ":".join(out)
 
 class image(fedd_rpc):
@@ -1302,4 +1465,6 @@
 	'vis': vis(),\
 	'info': exp_data(),\
+	'multiinfo': multi_exp_data(),\
+	'multistatus': multi_status(),\
 	'image': image(),\
 	'ns_image': ns_image(),\
Index: fedd/federation/experiment_control.py
===================================================================
--- fedd/federation/experiment_control.py	(revision d27fd76d6b9be5d68a54cd3f7c755d9f1e4f4738)
+++ fedd/federation/experiment_control.py	(revision 65f3f29cb7ca0c59eb17efb5e2786c359903a4a7)
@@ -329,4 +329,5 @@
                 'Vis': soap_handler('Vis', self.get_vis),
                 'Info': soap_handler('Info', self.get_info),
+                'MultiInfo': soap_handler('MultiInfo', self.get_multi_info),
                 'Terminate': soap_handler('Terminate', 
                     self.terminate_experiment),
@@ -338,4 +339,5 @@
                 'Vis': xmlrpc_handler('Vis', self.get_vis),
                 'Info': xmlrpc_handler('Info', self.get_info),
+                'MultiInfo': xmlrpc_handler('MultiInfo', self.get_multi_info),
                 'Terminate': xmlrpc_handler('Terminate',
                     self.terminate_experiment),
@@ -2177,4 +2179,29 @@
 		raise service_error(service_error.req, "No such experiment")
 
+    def clean_info_response(self, rv):
+	"""
+	Remove the information in the experiment's state object that is not in
+	the info response.
+	"""
+	# Remove the owner info (should always be there, but...)
+	if rv.has_key('owner'): del rv['owner']
+
+	# Convert the log into the allocationLog parameter and remove the
+	# log entry (with defensive programming)
+	if rv.has_key('log'):
+	    rv['allocationLog'] = "".join(rv['log'])
+	    del rv['log']
+	else:
+	    rv['allocationLog'] = ""
+
+	if rv['experimentStatus'] != 'active':
+	    if rv.has_key('federant'): del rv['federant']
+	else:
+	    # remove the allocationID info from each federant
+	    for f in rv.get('federant', []):
+		if f.has_key('allocID'): del f['allocID']
+
+	return rv
+
     def get_info(self, req, fid):
         """
@@ -2186,5 +2213,5 @@
         if not req:
             raise service_error(service_error.req,
-                    "Bad request format (no VisRequestBody)")
+                    "Bad request format (no InfoRequestBody)")
         exp = req.get('experiment', None)
         if exp:
@@ -2211,25 +2238,24 @@
 
 	if rv:
-	    # Remove the owner info (should always be there, but...)
-	    if rv.has_key('owner'): del rv['owner']
-
-	    # Convert the log into the allocationLog parameter and remove the
-	    # log entry (with defensive programming)
-	    if rv.has_key('log'):
-		rv['allocationLog'] = "".join(rv['log'])
-		del rv['log']
-	    else:
-		rv['allocationLog'] = ""
-
-	    if rv['experimentStatus'] != 'active':
-		if rv.has_key('federant'): del rv['federant']
-	    else:
-		# remove the allocationID info from each federant
-		for f in rv.get('federant', []):
-		    if f.has_key('allocID'): del f['allocID']
-
-	    return rv
+	    return self.clean_info_response(rv)
         else:
 	    raise service_error(service_error.req, "No such experiment")
+
+    def get_multi_info(self, req, fid):
+        """
+        Return all the stored info that this fedid can access
+        """
+	rv = { 'info': [ ] }
+
+	self.state_lock.acquire()
+	for key in [ k for k in self.state.keys() if isinstance(k, fedid)]:
+	    self.check_experiment_access(fid, key)
+
+	    if self.state.has_key(key):
+		e = copy.deepcopy(self.state[key])
+		e = self.clean_info_response(e)
+		rv['info'].append(e)
+        self.state_lock.release()
+	return rv
 
 
Index: fedd/wsdl/fedd.wsdl
===================================================================
--- fedd/wsdl/fedd.wsdl	(revision d27fd76d6b9be5d68a54cd3f7c755d9f1e4f4738)
+++ fedd/wsdl/fedd.wsdl	(revision 65f3f29cb7ca0c59eb17efb5e2786c359903a4a7)
@@ -60,4 +60,11 @@
   </message>
 
+  <message name="MultiInfoRequestMessage">
+    <part name="MultiInfoRequestBody" type="xsd1:multiInfoRequestType"/>
+  </message>
+
+  <message name="MultiInfoResponseMessage">
+    <part name="MultiInfoResponseBody" type="xsd1:multiInfoResponseType"/>
+  </message>
 
   <message name="TerminateRequestMessage">
@@ -128,4 +135,5 @@
       <fault name="VisFeddFault" message="tns:FaultMessage"/>
     </operation>
+
     <operation name="Info">
       <documentation>
@@ -137,4 +145,15 @@
       <fault name="InfoFeddFault" message="tns:FaultMessage"/>
     </operation>
+
+    <operation name="MultiInfo">
+      <documentation>
+	A one-stop request for meta-data on the experiment.  Includes all the
+	info from a Vtopo and a Vis request.
+      </documentation>
+      <input message="tns:MultiInfoRequestMessage"/>
+      <output message="tns:MultiInfoResponseMessage"/>
+      <fault name="MultiInfoFeddFault" message="tns:FaultMessage"/>
+    </operation>
+
     <operation name="Terminate">
       <documentation>
@@ -277,4 +296,25 @@
 	</output>
 	<fault name="InfoFeddFault">
+	  <soap:fault use="literal"  name="tns:FeddFault"
+	    namespace="http://www.isi.edu/faber/fedd.wsdl"
+	    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+	</fault>
+      </operation>
+      <operation name="MultiInfo">
+	<documentation>
+	  The bindings of this operation are straightforward SOAP RPC 1.1.
+	</documentation>
+	<soap:operation soapAction="MultiInfo"/> 
+	<input>
+	  <soap:body use="literal" parts="tns:MultiInfoRequestBody"
+	    namespace="http://www.isi.edu/faber/fedd.wsdl"
+	    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+	</input>
+	<output>
+	  <soap:body use="literal" parts="tns:MultiInfoResponseBody"
+	    namespace="http://www.isi.edu/faber/fedd.wsdl"
+	    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+	</output>
+	<fault name="MultiInfoFeddFault">
 	  <soap:fault use="literal"  name="tns:FeddFault"
 	    namespace="http://www.isi.edu/faber/fedd.wsdl"
Index: fedd/wsdl/fedd_types.xsd
===================================================================
--- fedd/wsdl/fedd_types.xsd	(revision d27fd76d6b9be5d68a54cd3f7c755d9f1e4f4738)
+++ fedd/wsdl/fedd_types.xsd	(revision 65f3f29cb7ca0c59eb17efb5e2786c359903a4a7)
@@ -561,4 +561,27 @@
       <xsd:element name="experimentAccess" type="tns:accessType" minOccurs="0"
 	maxOccurs="1"/>
+    </xsd:sequence>
+  </xsd:complexType>
+
+
+  <xsd:complexType name="multiInfoRequestType">
+    <xsd:annotation>
+      <xsd:documentation>
+	Gets all information that this user can access on this fedd.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+    </xsd:sequence>
+  </xsd:complexType>
+
+  <xsd:complexType name="multiInfoResponseType">
+    <xsd:annotation>
+      <xsd:documentation>
+	Multi info response.  A list of infoResponses
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:element name="info" type="tns:infoResponseType" minOccurs="0"
+	maxOccurs="unbounded"/>
     </xsd:sequence>
   </xsd:complexType>
