# Mark Huang <mlhuang@cs.princeton.edu>
# Copyright (C) 2006 The Trustees of Princeton University
#
-# $Id: Nodes.py,v 1.14 2006/10/25 14:29:13 mlhuang Exp $
+# $Id: Nodes.py,v 1.29 2007/01/09 16:13:36 mlhuang Exp $
#
from types import StringTypes
from PLC.Faults import *
from PLC.Parameter import Parameter
+from PLC.Filter import Filter
from PLC.Debug import profile
from PLC.Table import Row, Table
from PLC.NodeNetworks import NodeNetwork, NodeNetworks
table_name = 'nodes'
primary_key = 'node_id'
+ join_tables = ['nodegroup_node', 'conf_file_node', 'nodenetworks', 'pcu_node', 'slice_node', 'slice_attribute', 'node_session', 'peer_node']
fields = {
'node_id': Parameter(int, "Node identifier"),
'hostname': Parameter(str, "Fully qualified hostname", max = 255),
'site_id': Parameter(int, "Site at which this node is located"),
'boot_state': Parameter(str, "Boot state", max = 20),
- 'model': Parameter(str, "Make and model of the actual machine", max = 255),
+ 'model': Parameter(str, "Make and model of the actual machine", max = 255, nullok = True),
'boot_nonce': Parameter(str, "(Admin only) Random value generated by the node at last boot", max = 128),
'version': Parameter(str, "Apparent Boot CD version", max = 64),
'ssh_rsa_key': Parameter(str, "Last known SSH host key", max = 1024),
'last_updated': Parameter(int, "Date and time when node entry was created", ro = True),
'key': Parameter(str, "(Admin only) Node key", max = 256),
'session': Parameter(str, "(Admin only) Node session value", max = 256, ro = True),
- 'nodenetwork_ids': Parameter([int], "List of network interfaces that this node has", ro = True),
- 'nodegroup_ids': Parameter([int], "List of node groups that this node is in", ro = True),
- 'conf_file_ids': Parameter([int], "List of configuration files specific to this node", ro = True),
- # 'root_person_ids': Parameter([int], "(Admin only) List of people who have root access to this node", ro = True),
- 'slice_ids': Parameter([int], "List of slices on this node", ro = True),
- 'pcu_ids': Parameter([int], "List of PCUs that control this node", ro = True),
- 'ports': Parameter([int], "List of PCU ports that this node is connected to", ro = True),
+ 'nodenetwork_ids': Parameter([int], "List of network interfaces that this node has"),
+ 'nodegroup_ids': Parameter([int], "List of node groups that this node is in"),
+ 'conf_file_ids': Parameter([int], "List of configuration files specific to this node"),
+ # 'root_person_ids': Parameter([int], "(Admin only) List of people who have root access to this node"),
+ 'slice_ids': Parameter([int], "List of slices on this node"),
+ 'pcu_ids': Parameter([int], "List of PCUs that control this node"),
+ 'ports': Parameter([int], "List of PCU ports that this node is connected to"),
+ 'peer_id': Parameter(int, "Peer to which this node belongs", nullok = True),
+ 'peer_node_id': Parameter(int, "Foreign node identifier at peer", nullok = True),
}
+ # for Cache
+ class_key = 'hostname'
+ foreign_fields = ['boot_state','model','version']
+ # forget about these ones, they are read-only anyway
+ # handling them causes Cache to re-sync all over again
+ # 'date_created','last_updated'
+ foreign_xrefs = [
+ # in this case, we dont need the 'table' but Cache will look it up, so...
+ {'field' : 'site_id' , 'class' : 'Site' , 'table' : 'unused-on-direct-refs' } ,
+ ]
+
def validate_hostname(self, hostname):
if not valid_hostname(hostname):
raise PLCInvalidArgument, "Invalid hostname"
conflicts = Nodes(self.api, [hostname])
- for node_id, node in conflicts.iteritems():
- if 'node_id' not in self or self['node_id'] != node_id:
+ for node in conflicts:
+ if 'node_id' not in self or self['node_id'] != node['node_id']:
raise PLCInvalidArgument, "Hostname already in use"
return hostname
def validate_boot_state(self, boot_state):
- if boot_state not in BootStates(self.api):
+ boot_states = [row['boot_state'] for row in BootStates(self.api)]
+ if boot_state not in boot_states:
raise PLCInvalidArgument, "Invalid boot state"
return boot_state
+ validate_date_created = Row.validate_timestamp
+ validate_last_updated = Row.validate_timestamp
+
def delete(self, commit = True):
"""
Delete existing node.
assert 'node_id' in self
- # Delete all nodenetworks
- nodenetworks = NodeNetworks(self.api, self['nodenetwork_ids'])
- for nodenetwork in nodenetworks.values():
- nodenetwork.delete(commit = False)
-
# Clean up miscellaneous join tables
- for table in ['nodegroup_node', 'slice_node', 'slice_attribute', 'node_session']:
- self.api.db.do("DELETE FROM %s" \
- " WHERE node_id = %d" % \
+ for table in self.join_tables:
+ self.api.db.do("DELETE FROM %s WHERE node_id = %d" % \
(table, self['node_id']))
# Mark as deleted
self['deleted'] = True
self.sync(commit)
+
class Nodes(Table):
"""
Representation of row(s) from the nodes table in the
database.
"""
- def __init__(self, api, node_id_or_hostname_list = None):
- self.api = api
+ def __init__(self, api, node_filter = None, columns = None):
+ Table.__init__(self, api, Node, columns)
sql = "SELECT %s FROM view_nodes WHERE deleted IS False" % \
- ", ".join(Node.fields)
-
- if node_id_or_hostname_list:
- # Separate the list into integers and strings
- node_ids = filter(lambda node_id: isinstance(node_id, (int, long)),
- node_id_or_hostname_list)
- hostnames = filter(lambda hostname: isinstance(hostname, StringTypes),
- node_id_or_hostname_list)
- sql += " AND (False"
- if node_ids:
- sql += " OR node_id IN (%s)" % ", ".join(map(str, node_ids))
- if hostnames:
- sql += " OR hostname IN (%s)" % ", ".join(api.db.quote(hostnames)).lower()
- sql += ")"
-
- rows = self.api.db.selectall(sql)
-
- for row in rows:
- self[row['node_id']] = node = Node(api, row)
- for aggregate in ['nodenetwork_ids', 'nodegroup_ids',
- 'conf_file_ids', 'root_person_ids', 'slice_ids',
- 'pcu_ids']:
- if not node.has_key(aggregate) or node[aggregate] is None:
- node[aggregate] = []
- else:
- node[aggregate] = map(int, node[aggregate].split(','))
+ ", ".join(self.columns)
+
+ if node_filter is not None:
+ if isinstance(node_filter, (list, tuple, set)):
+ # Separate the list into integers and strings
+ ints = filter(lambda x: isinstance(x, (int, long)), node_filter)
+ strs = filter(lambda x: isinstance(x, StringTypes), node_filter)
+ node_filter = Filter(Node.fields, {'node_id': ints, 'hostname': strs})
+ sql += " AND (%s)" % node_filter.sql(api, "OR")
+ elif isinstance(node_filter, dict):
+ node_filter = Filter(Node.fields, node_filter)
+ sql += " AND (%s)" % node_filter.sql(api, "AND")
+
+ self.selectall(sql)