Merge remote-tracking branch 'origin/pycurl' into planetlab-4_0-branch
[plcapi.git] / PLC / NodeGroups.py
1 #
2 # Functions for interacting with the nodegroups table in the database
3 #
4 # Mark Huang <mlhuang@cs.princeton.edu>
5 # Copyright (C) 2006 The Trustees of Princeton University
6 #
7 # $Id: NodeGroups.py 5666 2007-11-06 21:52:21Z tmack $
8 #
9
10 from types import StringTypes
11
12 from PLC.Faults import *
13 from PLC.Parameter import Parameter, Mixed
14 from PLC.Filter import Filter
15 from PLC.Debug import profile
16 from PLC.Table import Row, Table
17 from PLC.Nodes import Node, Nodes
18
19 class NodeGroup(Row):
20     """
21     Representation of a row in the nodegroups table. To use, optionally
22     instantiate with a dict of values. Update as you would a
23     dict. Commit to the database with sync().
24     """
25
26     table_name = 'nodegroups'
27     primary_key = 'nodegroup_id'
28     join_tables = ['nodegroup_node', 'conf_file_nodegroup']
29     fields = {
30         'nodegroup_id': Parameter(int, "Node group identifier"),
31         'name': Parameter(str, "Node group name", max = 50),
32         'description': Parameter(str, "Node group description", max = 200, nullok = True),
33         'node_ids': Parameter([int], "List of nodes in this node group"),
34         'conf_file_ids': Parameter([int], "List of configuration files specific to this node group"),
35         }
36     related_fields = {
37         'conf_files': [Parameter(int, "ConfFile identifier")],
38         'nodes': [Mixed(Parameter(int, "Node identifier"),
39                         Parameter(str, "Fully qualified hostname"))]
40         }
41
42     def validate_name(self, name):
43         # Make sure name is not blank
44         if not len(name):
45                 raise PLCInvalidArgument, "Invalid node group name"
46         
47         # Make sure node group does not alredy exist
48         conflicts = NodeGroups(self.api, [name])
49         for nodegroup in conflicts:
50             if 'nodegroup_id' not in self or self['nodegroup_id'] != nodegroup['nodegroup_id']:
51                raise PLCInvalidArgument, "Node group name already in use"
52
53         return name
54
55     def add_node(self, node, commit = True):
56         """
57         Add node to existing nodegroup.
58         """
59
60         assert 'nodegroup_id' in self
61         assert isinstance(node, Node)
62         assert 'node_id' in node
63
64         node_id = node['node_id']
65         nodegroup_id = self['nodegroup_id']
66
67         if node_id not in self['node_ids']:
68             assert nodegroup_id not in node['nodegroup_ids']
69
70             self.api.db.do("INSERT INTO nodegroup_node (nodegroup_id, node_id)" \
71                            " VALUES(%(nodegroup_id)d, %(node_id)d)",
72                            locals())
73
74             if commit:
75                 self.api.db.commit()
76
77             self['node_ids'].append(node_id)
78             node['nodegroup_ids'].append(nodegroup_id)
79
80     def remove_node(self, node, commit = True):
81         """
82         Remove node from existing nodegroup.
83         """
84
85         assert 'nodegroup_id' in self
86         assert isinstance(node, Node)
87         assert 'node_id' in node
88
89         node_id = node['node_id']
90         nodegroup_id = self['nodegroup_id']
91
92         if node_id in self['node_ids']:
93             assert nodegroup_id in node['nodegroup_ids']
94
95             self.api.db.do("DELETE FROM nodegroup_node" \
96                            " WHERE nodegroup_id = %(nodegroup_id)d" \
97                            " AND node_id = %(node_id)d",
98                            locals())
99
100             if commit:
101                 self.api.db.commit()
102
103             self['node_ids'].remove(node_id)
104             node['nodegroup_ids'].remove(nodegroup_id)
105
106     def associate_nodes(self, auth, field, value):
107         """
108         Adds nodes found in value list to this nodegroup (using AddNodeToNodeGroup).
109         Deletes nodes not found in value list from this slice (using DeleteNodeFromNodeGroup).
110         """
111
112         assert 'node_ids' in self
113         assert 'nodegroup_id' in self
114         assert isinstance(value, list)
115
116         (node_ids, hostnames) = self.separate_types(value)[0:2]
117
118         # Translate hostnames into node_ids
119         if hostnames:
120             nodes = Nodes(self.api, hostnames, ['node_id']).dict('node_id')
121             node_ids += nodes.keys()
122
123         # Add new ids, remove stale ids
124         if self['node_ids'] != node_ids:
125             from PLC.Methods.AddNodeToNodeGroup import AddNodeToNodeGroup
126             from PLC.Methods.DeleteNodeFromNodeGroup import DeleteNodeFromNodeGroup
127             new_nodes = set(node_ids).difference(self['node_ids'])
128             stale_nodes = set(self['node_ids']).difference(node_ids)
129
130             for new_node in new_nodes:
131                 AddNodeToNodeGroup.__call__(AddNodeToNodeGroup(self.api), auth, new_node, self['nodegroup_id'])
132             for stale_node in stale_nodes:
133                 DeleteNodeFromNodeGroup.__call__(DeleteNodeFromNodeGroup(self.api), auth, stale_node, self['nodegroup_id'])
134
135     def associate_conf_files(self, auth, field, value):
136         """
137         Add conf_files found in value list (AddConfFileToNodeGroup)
138         Delets conf_files not found in value list (DeleteConfFileFromNodeGroup)
139         """
140
141         assert 'conf_file_ids' in self
142         assert 'nodegroup_id' in self
143         assert isinstance(value, list)
144
145         conf_file_ids = self.separate_types(value)[0]
146
147         if self['conf_file_ids'] != conf_file_ids:
148             from PLC.Methods.AddConfFileToNodeGroup import AddConfFileToNodeGroup
149             from PLC.Methods.DeleteConfFileFromNodeGroup import DeleteConfFileFromNodeGroup
150             new_conf_files = set(conf_file_ids).difference(self['conf_file_ids'])
151             stale_conf_files = set(self['conf_file_ids']).difference(conf_file_ids)
152
153             for new_conf_file in new_conf_files:
154                 AddConfFileToNodeGroup.__call__(AddConfFileToNodeGroup(self.api), auth, new_conf_file, self['nodegroup_id'])
155             for stale_conf_file in stale_conf_files:
156                 DeleteConfFileFromNodeGroup.__call__(DeleteConfFileFromNodeGroup(self.api), auth, stale_conf_file, self['nodegroup_id'])
157
158
159 class NodeGroups(Table):
160     """
161     Representation of row(s) from the nodegroups table in the
162     database.
163     """
164
165     def __init__(self, api, nodegroup_filter = None, columns = None):
166         Table.__init__(self, api, NodeGroup, columns)
167
168         sql = "SELECT %s FROM view_nodegroups WHERE True" % \
169               ", ".join(self.columns)
170
171         if nodegroup_filter is not None:
172             if isinstance(nodegroup_filter, (list, tuple, set)):
173                 # Separate the list into integers and strings
174                 ints = filter(lambda x: isinstance(x, (int, long)), nodegroup_filter)
175                 strs = filter(lambda x: isinstance(x, StringTypes), nodegroup_filter)
176                 nodegroup_filter = Filter(NodeGroup.fields, {'nodegroup_id': ints, 'name': strs})
177                 sql += " AND (%s) %s" % nodegroup_filter.sql(api, "OR")
178             elif isinstance(nodegroup_filter, dict):
179                 nodegroup_filter = Filter(NodeGroup.fields, nodegroup_filter)
180                 sql += " AND (%s) %s" % nodegroup_filter.sql(api, "AND")
181
182         self.selectall(sql)