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_index = local_foreign_nodes.dict('hostname')
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 remote_fields = ['boot_state','model','version','date_created','last_updated']
125 ### scan the new entries, and mark them uptodate
126 for node in peer_get_nodes:
127 hostname = node['hostname']
128 foreign_id = node ['node_id']
130 foreign_node = local_foreign_nodes_index[hostname]
131 if foreign_node['peer_id'] != peer_id:
132 ### the node has changed its plc, needs to update peer_node
133 old_peer_id = foreign_node['peer_id']
134 old_peers=Peers(self.api,[peer_id])
136 # remove from previous peer
137 old_peers[0].manage_node(foreign_node,None,False)
139 self.manage_node(foreign_node,foreign_id,True)
140 foreign_node['peer_id'] = peer_id
141 ### update it anyway: copy other relevant fields
142 for field in remote_fields:
143 foreign_node[field]=node[field]
144 # this row is now valid
145 foreign_node.uptodate=True
148 new_foreign_node = ForeignNode(self.api, {'hostname':hostname})
149 for field in remote_fields:
150 new_foreign_node[field]=node[field]
151 ### need to sync so we get a node_id
152 new_foreign_node.sync()
153 new_foreign_node.uptodate = True
154 self.manage_node(new_foreign_node,foreign_id,True)
155 local_foreign_nodes_index[hostname]=new_foreign_node
157 ### delete entries that are not uptodate
158 for foreign_node in local_foreign_nodes:
159 if not foreign_node.uptodate:
160 foreign_node.delete()
162 return len(peer_get_nodes)-old_count
164 def refresh_slices (self, peer_get_slices):
170 Maps to the peers table in the database
173 def __init__ (self, api, peer_filter = None, columns = None):
174 Table.__init__(self, api, Peer, columns)
176 sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
177 ", ".join(self.columns)
179 if peer_filter is not None:
180 if isinstance(peer_filter, (list, tuple, set)):
181 # Separate the list into integers and strings
182 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
183 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
184 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
185 sql += " AND (%s)" % peer_filter.sql(api, "OR")
186 elif isinstance(peer_filter, dict):
187 peer_filter = Filter(Peer.fields, peer_filter)
188 sql += " AND (%s)" % peer_filter.sql(api, "AND")