- Timestamp:
- May 14, 2010 10:33:32 AM (15 years ago)
- Branches:
- axis_example, compt_changes, info-ops, master, version-3.01, version-3.02
- Children:
- da5b93c
- Parents:
- afc4af4
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
fedd/compose.py
rafc4af4 rb0581ac 10 10 11 11 class constraint: 12 def __init__(self, required=False, accepts=None, provides=None): 12 def __init__(self, name=None, required=False, accepts=None, provides=None, 13 match=None): 14 self.name = name 13 15 self.required = required 14 16 self.accepts = accepts or [] 15 17 self.provides = provides or [] 18 self.match = match 16 19 17 20 def __str__(self): 18 return "%s:%s:%s" % (self.required, ",".join(self.provides), 19 ",".join(self.accepts)) 20 21 22 def add_to_map(exp, mark): 23 valid_marks = ('stub', 'transit') 24 21 return "%s:%s:%s:%s" % (self.name, self.required, 22 ",".join(self.provides), ",".join(self.accepts)) 23 24 25 def add_to_map(exp, mark, provides, accepts): 26 """ 27 Create a constraint from a string of the form 28 name:required:provides:accepts 29 where name is the name of the node in the current topology, if the second 30 field is "required" this is a required constraint, a list of attributes 31 that this connection point provides and a list that it accepts. The 32 provides and accepts lists are comma-separated. Note that the string 33 passed in as exp should have whitespace removed. The constraint is added to 34 the mark dict under the name key and entries in the provides and accepts 35 are made to teh constraing for each attribute that it provides or accepts. 36 """ 25 37 nn, r, p, a = exp.split(":") 26 38 p = p.split(",") 27 39 a = a.split(",") 28 mark[nn] = constraint(r == 'required', p, a) 29 30 def annotate_topo(top, mark): 31 def make_new_name(names): 32 i = 0 33 n = "mark%d" % i 34 while n in names: 35 i += 1 36 n = "mark%d" % i 37 names.add(n) 38 return n 39 40 snames = set([ s.name for s in top.substrates ]) 41 40 c = constraint(name=nn, required=(r == 'required'), provides=p, accepts=a) 41 mark[nn] = c 42 43 for attr, dict in ((p, provides), (a, accepts)): 44 for a in attr: 45 if a not in dict: dict[a] = [c] 46 else: dict[a].append(c) 47 48 def make_new_name(names, prefix="name"): 49 """ 50 Generate an identifier not present in names by appending an integer to 51 prefix. The new name is inserted into names and returned. 52 """ 53 i = 0 54 n = "%s%d" % (prefix,i) 55 while n in names: 56 i += 1 57 n = "%s%d" % (prefix,i) 58 names.add(n) 59 return n 60 61 def add_interfaces(top, mark): 62 """ 63 Add unconnected interfaces to the nodes whose names are keys in the mark 64 dict. As the interface is added change the name field of the contstraint 65 in mark into a tuple containing the node name, interface name, and topology 66 in which they reside. 67 """ 42 68 for e in top.elements: 43 for n in e.name: 44 if n in mark: 45 if all([ not i.get_attribute('export') for i in e.interface]): 46 con = mark[n] 47 inames = set([x.name for x in e.interface]) 48 sn = make_new_name(snames) 49 ii = make_new_name(inames) 50 s = topdl.Substrate(name=sn) 51 i = topdl.Interface(substrate=sn, name=ii, element=e, 52 attribute=[ 53 topdl.Attribute(attribute='composition_point', 54 value='True'), 55 topdl.Attribute(attribute='required', 56 value = "%s" % con.required), 57 topdl.Attribute(attribute='provides', 58 value=",".join(con.provides)), 59 topdl.Attribute(attribute='accepts', 60 value=",".join(con.accepts)), 61 ] 62 ) 63 e.interface.append(i) 64 top.substrates.append(s) 65 66 top.incorporate_elements() 69 if e.name in mark: 70 con = mark[e.name] 71 ii = make_new_name(set([x.name for x in e.interface]), "inf") 72 e.interface.append( 73 topdl.Interface(substrate=[], name=ii, element=e)) 74 con.name = (e.name, ii, top) 75 76 def localize_names(top, names, marks): 77 """ 78 Take a topology and rename any substrates or elements that share a name 79 with an existing computer or substrate. Keep the original name as a 80 localized_name attribute. In addition, remap any constraints or interfaces 81 that point to the old name over to the new one. Constraints are found in 82 the marks dict, indexed by node name. Those constraints name attributes 83 have already been converted to triples (node name, interface name, 84 topology) so only the node name needs to be changed. 85 """ 86 sub_map = { } 87 for s in top.substrates: 88 s.set_attribute('localized_name', s.name) 89 if s.name in names: 90 sub_map[s.name] = n = make_new_name(names, "substrate") 91 s.name = n 92 else: 93 names.add(s.name) 94 95 for e in [ e for e in top.elements if isinstance(e, topdl.Computer)]: 96 e.set_attribute('localized_name', e.name) 97 if e.name in names: 98 nn= make_new_name(names, "computer") 99 if e.name in marks: 100 n, i, t = marks[e.name].name 101 marks[e.name].name = (nn, i, t) 102 e.name = nn 103 else: 104 names.add(e.name) 105 106 # Interface mapping. The list comprehension creates a list of 107 # substrate names where each element in the list is replaced by the 108 # entry in sub_map indexed by it if present and left alone otherwise. 109 for i in e.interface: 110 i.substrate = [ sub_map.get(ii, ii) for ii in i.substrate ] 111 112 def meet_constraints(candidates, provides, accepts): 113 """ 114 Try to meet all the constraints in candidates using the information in the 115 provides and accepts dicts (which index constraints that provide or accept 116 the given attribute). A constraint is met if it can be matched with another 117 constraint that provides an attribute that the first constraint accepts. 118 Only one match per pair is allowed, and we always prefer matching a 119 required constraint to an unreqired one. If all the candidates can be 120 matches, return True and return False otherwise. 121 """ 122 got_all = True 123 for c in candidates: 124 if not c.match: 125 match = None 126 for a in c.accepts: 127 for can in provides.get(a,[]): 128 # A constraint cannot satisfy itself, nor do we allow loops 129 # within the same topology. This also excludes matched 130 # constraints. 131 if can.name != c.name and can.name[2] != c.name[2] and \ 132 not can.match: 133 match = can 134 # Within providers, prefer matches against required 135 # composition points 136 if can.required: 137 break 138 # Within acceptance categories, prefer matches against required 139 # composition points 140 if match and match.required: 141 break 142 143 # Done checking all possible matches. 144 if match: 145 match.match = c 146 c.match = match 147 else: 148 got_all = False 149 return got_all 67 150 68 151 def remote_ns2topdl(uri, desc, cert): 152 """ 153 Call a remote service to convert the ns2 to topdl (and in fact all the way 154 to a topdl.Topology. 155 """ 69 156 70 157 req = { 'description' : { 'ns2description': desc }, } … … 85 172 return None 86 173 174 def connect_composition_points(top, contraints): 175 """ 176 top is a topology containing copies of all the topologies represented in 177 the contsraints, flattened into one name space. This routine inserts the 178 additional substrates required to interconnect the topology as described by 179 the constraints. After the links are made, unused connection points are 180 purged. 181 """ 182 done = set() 183 for c in constraints: 184 if c not in done and c.match: 185 # c is an unprocessed matched constraint. Create a substrate 186 # and attach it to the interfaces named by c and c.match. 187 sn = make_new_name(names, "sub") 188 s = topdl.Substrate(name=sn) 189 connected = 0 190 # Walk through all the computers in the topology 191 for e in [ e for e in top.elements \ 192 if isinstance(e, topdl.Computer)]: 193 # if the computer matches the computer and interface name in 194 # either c or c.match, connect it. Once both computers have 195 # been found and connected, exit the loop walking through all 196 # computers. 197 for comp, inf in (c.name[0:2], c.match.name[0:2]): 198 if e.name == comp: 199 for i in e.interface: 200 if i.name == inf: 201 i.substrate.append(sn) 202 connected += 1 203 break 204 break 205 # Connected both, so add the substrate to the topology 206 if connected == 2: 207 top.substrates.append(s) 208 break 209 # c and c.match have been processed, so add them to the done set 210 done.add(c) 211 done.add(c.match) 212 213 # All interfaces with matched constraints have been connected. Cull any 214 # interfaces unassigned to a substrate 215 for e in [ e for e in top.elements if isinstance(e, topdl.Computer)]: 216 if any([ not i.substrate for i in e.interface]): 217 e.interface = [ i for i in e.interface if i.substrate ] 218 219 top.incorporate_elements() 220 221 222 87 223 const_re = re.compile("\s*#\s*COMPOSITION:\s*([^:]+:[^:]+:.*)") 88 224 … … 94 230 95 231 opts, args = parser.parse_args() 232 96 233 97 234 if opts.cert: … … 102 239 cert = None 103 240 241 comps = [ ] 242 names = set() 243 constraints = [ ] 244 provides = { } 245 accepts = { } 104 246 for fn in args: 105 247 marks = { } … … 111 253 m = const_re.search(l) 112 254 if m: 113 add_to_map(re.sub('\s', '', m.group(1)), marks) 255 add_to_map(re.sub('\s', '', m.group(1)), marks, provides, 256 accepts) 114 257 except EnvironmentError, e: 115 print >>sys.stderr, "Error on %S: %s" % (fn, e) 116 117 print marks 258 print >>sys.stderr, "Error on %s: %s" % (fn, e) 259 continue 118 260 119 261 top = remote_ns2topdl(opts.url, contents, cert) 120 121 if top: 122 annotate_topo(top, marks) 123 print topdl.topology_to_xml(top) 124 else: 125 sys.exit("Topology conversion failed on %s" % fn) 262 add_interfaces(top, marks) 263 localize_names(top, names, marks) 264 top.incorporate_elements() 265 constraints.extend(marks.values()) 266 comps.append(top) 267 268 # Now the various components live in the same namespace and are marked with 269 # their composition requirements. 270 271 if not meet_constraints([c for c in constraints if c.required], 272 provides, accepts): 273 sys.exit("Could not meet all required constraints") 274 meet_constraints([ c for c in constraints if not c.match ], provides, accepts) 275 276 # Make a topology containing all elements and substrates from components that 277 # had matches. 278 comp = topdl.Topology() 279 for t in set([ c.name[2] for c in constraints if c.match]): 280 comp.elements.extend([e.clone() for e in t.elements]) 281 comp.substrates.extend([s.clone() for s in t.substrates]) 282 283 # Add substrates and connections corresponding to composition points 284 connect_composition_points(comp, constraints) 285 286 print topdl.topology_to_xml(comp, top='experiment')
Note: See TracChangeset
for help on using the changeset viewer.