2 # Thierry Parmentelat - INRIA
6 from types import StringTypes
8 from PLC.Faults import *
9 from PLC.Parameter import Parameter
10 from PLC.Filter import Filter
11 from PLC.Table import Row, Table
13 from PLC.ForeignNodes import ForeignNodes,ForeignNode
17 Stores the list of peering PLCs in the peers table.
18 See the Row class for more details
22 primary_key = 'peer_id'
24 'peer_id' : Parameter (int, "Peer identifier"),
25 'peername' : Parameter (str, "Peer name"),
26 'peer_url' : Parameter (str, "Peer API url"),
27 'person_id' : Parameter (int, "Person_id of the account storing credentials - temporary"),
28 'node_ids' : Parameter ([int], "This peer's nodes ids")
31 def validate_peer_url (self, url):
33 Validate URL, checks it looks like https
35 invalid_url = PLCInvalidArgument("Invalid URL")
36 if not re.compile ("^https://.*$").match(url) :
40 def delete (self, commit=True):
45 assert 'peer_id' in self
47 # remove nodes depending on this peer
48 for foreign_node_id in self.get_foreign_nodes():
50 foreign_node = ForeignNodes(self.api,[foreign_node_id])[0]
51 foreign_node.delete(commit)
53 print "Glitch : a foreign node instance was uncleanly deleted"
56 self['deleted'] = True
59 def get_foreign_nodes (self):
61 returns a list of the foreign nodes in this peer
63 sql="SELECT node_ids FROM peer_nodes WHERE peer_id=%d"%self['peer_id']
64 node_ids = self.api.db.selectall(sql)
65 return node_ids[0]['node_ids']
67 def manage_node (self, foreign_node, foreign_id, commit=True):
69 associate/dissociate a foreign node to/from a peer
70 foreign_node is a local object that describes a remote node
71 foreign_id is the unique id as provided by the remote peer
73 if foreign_id is None : performs dissociation
74 otherwise: performs association
77 assert 'peer_id' in self
78 assert 'node_id' in foreign_node
80 peer_id = self['peer_id']
81 node_id = foreign_node ['node_id']
85 sql = "INSERT INTO peer_node VALUES (%d,%d,%d)" % (peer_id,node_id,foreign_id)
87 if self['node_ids'] is None:
88 self['node_ids']=[node_id,]
89 self['node_ids'].append(node_id)
92 sql = "DELETE FROM peer_node WHERE peer_id=%d AND node_id=%d" % (peer_id,node_id)
94 self['node_ids'].remove(node_id)
99 def refresh_nodes (self, peer_get_nodes):
101 refreshes the foreign_nodes and peer_node tables
102 expected input is the current list of nodes as returned by GetNodes
104 returns the number of new nodes on this peer (can be negative)
107 peer_id = self['peer_id']
109 # we get the whole table just in case
110 # a host would have switched from one plc to the other
111 local_foreign_nodes = ForeignNodes (self.api)
112 # new to index it by hostname for searching later
113 local_foreign_nodes.hostname_index()
115 ### mark entries for this peer outofdate
117 for foreign_node in local_foreign_nodes:
118 if foreign_node['peer_id'] == peer_id:
119 foreign_node.uptodate=False
122 ### these fields get copied through
123 ### xxx need to figure how to revert unix timestamp to db timestamp format
124 remote_fields = ['boot_state','model','version','date_created','last_updated']
126 ### scan the new entries, and mark them uptodate
127 for node in peer_get_nodes:
128 hostname = node['hostname']
129 foreign_id = node ['node_id']
131 foreign_node = local_foreign_nodes.hostname_locate(hostname)
132 if foreign_node['peer_id'] != peer_id:
133 ### the node has changed its plc, needs to update peer_node
134 old_peer_id = foreign_node['peer_id']
135 old_peers=Peers(self.api,[peer_id])
137 # remove from previous peer
138 old_peers[0].manage_node(foreign_node,None,False)
140 self.manage_node(foreign_node,foreign_id,True)
141 foreign_node['peer_id'] = peer_id
142 ### update it anyway: copy other relevant fields
143 for field in remote_fields:
144 foreign_node[field]=node[field]
145 # this row is now valid
146 foreign_node.uptodate=True
149 new_foreign_node = ForeignNode(self.api, {'hostname':hostname})
150 for field in remote_fields:
151 new_foreign_node[field]=node[field]
152 ### need to sync so we get a node_id
153 new_foreign_node.sync()
154 new_foreign_node.uptodate = True
155 self.manage_node(new_foreign_node,foreign_id,True)
156 local_foreign_nodes.hostname_add_by(new_foreign_node)
158 ### delete entries that are not uptodate
159 for foreign_node in local_foreign_nodes:
160 if not foreign_node.uptodate:
161 foreign_node.delete()
163 return len(peer_get_nodes)-old_count
165 def refresh_slices (self, peer_get_slices):
171 Maps to the peers table in the database
174 def __init__ (self, api, peer_filter = None, columns = None):
175 Table.__init__(self, api, Peer, columns)
177 sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
178 ", ".join(self.columns)
180 if peer_filter is not None:
181 if isinstance(peer_filter, (list, tuple, set)):
182 # Separate the list into integers and strings
183 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
184 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
185 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
186 sql += " AND (%s)" % peer_filter.sql(api, "OR")
187 elif isinstance(peer_filter, dict):
188 peer_filter = Filter(Peer.fields, peer_filter)
189 sql += " AND (%s)" % peer_filter.sql(api, "AND")