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.Nodes import Nodes,Node
14 from PLC.Slices import Slices,Slice
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"),
30 'slice_ids' : Parameter ([int], "This peer's slices ids"),
33 def validate_peer_url (self, url):
35 Validate URL, checks it looks like https
37 invalid_url = PLCInvalidArgument("Invalid URL")
38 if not re.compile ("^https://.*$").match(url) :
42 def delete (self, commit=True):
47 assert 'peer_id' in self
49 # remove nodes depending on this peer
50 for foreign_node in Nodes (self.api, self.get_node_ids()):
51 foreign_node.delete(commit)
54 self['deleted'] = True
57 def get_node_ids (self):
59 returns a list of the node ids in this peer
61 sql="SELECT node_ids FROM peer_nodes WHERE peer_id=%d"%self['peer_id']
62 node_ids = self.api.db.selectall(sql)
63 return node_ids[0]['node_ids']
65 def refresh_nodes (self, peer_get_nodes):
67 refreshes the foreign_nodes and peer_node tables
68 expected input is the current list of nodes as returned by GetNodes
70 returns the number of new nodes on this peer (can be negative)
73 peer_id = self['peer_id']
75 # we get the whole table just in case
76 # a host would have switched from one plc to the other
77 local_foreign_nodes = Nodes (self.api,None,None,'foreign')
79 # index it by hostname for searching later
80 #local_foreign_nodes_index=local_foreign_nodes.dict('hostname')
81 local_foreign_nodes_index={}
82 for node in local_foreign_nodes:
83 local_foreign_nodes_index[node['hostname']]=node
85 ### mark entries for this peer outofdate
87 for foreign_node in local_foreign_nodes:
88 if foreign_node['peer_id'] == peer_id:
89 foreign_node.uptodate=False
92 ### these fields get copied through
93 remote_fields = ['boot_state','model','version','date_created','last_updated']
95 ### scan the new entries, and mark them uptodate
96 for node in peer_get_nodes:
97 hostname = node['hostname']
99 foreign_node = local_foreign_nodes_index[hostname]
100 if foreign_node['peer_id'] != peer_id:
101 # ### the node has changed its plc
102 foreign_node['peer_id'] = peer_id
103 ### update it anyway: copy other relevant fields
104 for field in remote_fields:
105 foreign_node[field]=node[field]
106 # this row is now valid
107 foreign_node.uptodate=True
110 new_foreign_node = Node(self.api, {'hostname':hostname})
111 new_foreign_node['peer_id']=peer_id
112 for field in remote_fields:
113 new_foreign_node[field]=node[field]
114 ### need to sync so we get a node_id
115 new_foreign_node.sync()
116 new_foreign_node.uptodate = True
117 # self.manage_node(new_foreign_node,True,True)
118 local_foreign_nodes_index[hostname]=new_foreign_node
120 ### delete entries that are not uptodate
121 for foreign_node in local_foreign_nodes:
122 if not foreign_node.uptodate:
123 foreign_node.delete()
125 return len(peer_get_nodes)-old_count
127 ### transcode node_id
128 def locate_alien_node_id_in_foreign_nodes (self, peer_foreign_nodes_dict, alien_id):
130 returns a local node_id as transcoded from an alien node_id
131 only lookups our local nodes because we dont need to know about other sites
132 returns a valid local node_id, or throws an exception
134 peer_foreign_node = peer_foreign_nodes_dict[alien_id]
135 hostname = peer_foreign_node['hostname']
136 return Nodes(self.api,[hostname])[0]['node_id']
138 def refresh_slices (self, peer_get_slices, peer_foreign_nodes):
140 refreshes the foreign_slices and peer_slice tables
141 expected input is the current list of slices as returned by GetSlices
143 returns the number of new slices on this peer (can be negative)
146 peer_id = self['peer_id']
148 # we get the whole table just in case
149 # a host would have switched from one plc to the other
150 local_foreign_slices = Slices (self.api,{'~peer_id':None})
151 # index it by name for searching later
152 local_foreign_slices_index = local_foreign_slices.dict('name')
154 ### mark entries for this peer outofdate
156 for foreign_slice in local_foreign_slices:
157 if foreign_slice['peer_id'] == peer_id:
158 foreign_slice.uptodate=False
161 ### these fields get copied through
162 remote_fields = ['instantiation', 'url', 'description',
163 'max_nodes', 'created', 'expires']
165 ### scan the new entries, and mark them uptodate
167 for slice in peer_get_slices:
169 ### ignore system-wide slices
170 if slice['creator_person_id'] == 1:
177 foreign_slice = local_foreign_slices_index[name]
178 if foreign_slice['peer_id'] != peer_id:
179 # more suspucious ? - the slice moved on another peer
180 foreign_slice['peer_id'] = peer_id;
182 foreign_slice = Slice(self.api, {'name':name})
183 foreign_slice['peer_id']=self['peer_id']
185 # foreign_slice['site_id']=1
186 ### need to sync so we get a slice_id
189 local_foreign_slices_index[name]=foreign_slice
192 for field in remote_fields:
193 foreign_slice[field]=slice[field]
194 # this row is now valid
195 foreign_slice.uptodate=True
200 # in slice we get a set of node_ids
201 # but these ids are RELATIVE TO THE PEER
202 # so we need to figure the local node_id for these nodes
203 # we do this through peer_foreign_nodes
205 peer_foreign_nodes_dict = {}
206 for foreign_node in peer_foreign_nodes:
207 peer_foreign_nodes_dict[foreign_node['node_id']]=foreign_node
208 updated_node_ids = []
209 for alien_node_id in slice['node_ids']:
211 local_node_id=self.locate_alien_node_id_in_foreign_nodes(peer_foreign_nodes_dict,
213 updated_node_ids.append(local_node_id)
215 # this node_id is not in our scope
217 foreign_slice.update_slice_nodes (updated_node_ids)
219 ### delete entries that are not uptodate
220 for foreign_slice in local_foreign_slices:
221 if not foreign_slice.uptodate:
222 foreign_slice.delete()
224 return new_count-old_count
228 Maps to the peers table in the database
231 def __init__ (self, api, peer_filter = None, columns = None):
232 Table.__init__(self, api, Peer, columns)
234 sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
235 ", ".join(self.columns)
237 if peer_filter is not None:
238 if isinstance(peer_filter, (list, tuple, set)):
239 # Separate the list into integers and strings
240 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
241 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
242 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
243 sql += " AND (%s)" % peer_filter.sql(api, "OR")
244 elif isinstance(peer_filter, dict):
245 peer_filter = Filter(Peer.fields, peer_filter)
246 sql += " AND (%s)" % peer_filter.sql(api, "AND")