2 # Thierry Parmentelat - INRIA
7 from types import StringTypes
9 from PLC.Faults import *
10 from PLC.Parameter import Parameter
11 from PLC.Filter import Filter
12 from PLC.Table import Row, Table
14 from PLC.ForeignNodes import ForeignNodes,ForeignNode
18 Stores the list of peering PLCs in the peers table.
19 See the Row class for more details
23 primary_key = 'peer_id'
25 'peer_id' : Parameter (int, "Peer identifier"),
26 'peername' : Parameter (str, "Peer name"),
27 'peer_url' : Parameter (str, "Peer API url"),
28 'person_id' : Parameter (int, "Person_id of the account storing credentials - temporary"),
29 'node_ids' : Parameter ([int], "This peer's nodes ids")
32 def validate_peer_url (self, url):
34 Validate URL, checks it looks like https
36 invalid_url = PLCInvalidArgument("Invalid URL")
37 if not re.compile ("^https://.*$").match(url) :
41 def manage_node (self, foreign_node, add_if_true_del_if_false=True, commit=True):
43 Add foreign node to a peer
46 assert 'peer_id' in self
47 assert 'node_id' in foreign_node
49 peer_id = self['peer_id']
50 node_id = foreign_node ['node_id']
52 if add_if_true_del_if_false:
54 sql = "INSERT INTO peer_node VALUES (%d,%d)" % (peer_id,node_id)
56 if self['node_ids'] is None:
57 self['node_ids']=[node_id,]
58 self['node_ids'].append(node_id)
61 sql = "DELETE FROM peer_node WHERE peer_id=%d AND node_id=%d" % (peer_id,node_id)
63 self['node_ids'].remove(node_id)
68 def refresh_nodes (self, current_peer_nodes):
70 refreshes the foreign_nodes and peer_node tables
71 expected input is the current list of nodes as returned by GetNodes
73 returns the number of new nodes on this peer (can be negative)
76 peer_id = self['peer_id']
78 # we get the whole table just in case
79 # a host would have switched from one plc to the other
80 local_foreign_nodes = ForeignNodes (self.api)
82 ### mark entries for this peer outofdate
84 for foreign_node in local_foreign_nodes:
85 if foreign_node['peer_id'] == peer_id:
86 foreign_node.uptodate=False
89 ### these fields get copied through
90 ### xxx need to figure how to revert unix timestamp to db timestamp format
91 # remote_fields = ['boot_state','model','version','date_created','last_updated']
92 remote_fields = ['boot_state','model','version']
94 ### scan the new entries, and mark them uptodate
95 for node in current_peer_nodes:
96 hostname = node['hostname']
98 foreign_node = ForeignNodes(self.api,{'hostname':hostname})[0]
99 if foreign_node['peer_id'] != peer_id:
100 ### the node has changed its plc, needs to update peer_node
101 old_peer_id = foreign_node['peer_id']
102 old_peers=Peers(self.api,[peer_id])
104 old_peers[0].manage_node(foreign_node,False)
105 self.manage_node(foreign_node,True)
106 foreign_node['peer_id'] = peer_id
107 ### update it anyway: copy other relevant fields
108 for field in remote_fields:
109 foreign_node[field]=node[field]
110 # this row is now valid
111 foreign_node.uptodate=True
114 new_foreign_node = ForeignNode(self.api, {'hostname':hostname})
115 for field in remote_fields:
116 new_foreign_node[field]=node[field]
117 ### need to sync so we get a node_id
118 new_foreign_node.sync()
119 new_foreign_node.uptodate = True
120 self.manage_node(new_foreign_node,True)
123 ### delete entries that are not uptodate
124 for foreign_node in local_foreign_nodes:
125 if not foreign_node.uptodate:
126 foreign_node.delete()
128 return len(current_peer_nodes)-old_count
131 def delete (self, commit=True):
136 assert 'peer_id' in self
138 self['deleted'] = True
143 Maps to the peers table in the database
146 def __init__ (self, api, peer_filter = None, columns = None):
147 Table.__init__(self, api, Peer, columns)
149 sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
150 ", ".join(self.columns)
152 if peer_filter is not None:
153 if isinstance(peer_filter, (list, tuple, set)):
154 # Separate the list into integers and strings
155 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
156 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
157 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
158 sql += " AND (%s)" % peer_filter.sql(api, "OR")
159 elif isinstance(peer_filter, dict):
160 peer_filter = Filter(Peer.fields, peer_filter)
161 sql += " AND (%s)" % peer_filter.sql(api, "AND")