import re from PLC.Faults import * from PLC.Auth import Auth from PLC.Method import Method from PLC.Parameter import Parameter, Mixed from PLC.Table import Row from PLC.Slices import Slice, Slices from PLC.Sites import Site, Sites from PLC.TagTypes import TagTypes from PLC.SliceTags import SliceTags from PLC.Methods.AddSliceTag import AddSliceTag from PLC.Methods.UpdateSliceTag import UpdateSliceTag can_update = ['name', 'instantiation', 'url', 'description', 'max_nodes'] # it would be much better to have this configured from the outside # this is not, however, the subject of the current exercise # so let's keep it simple for now VLAN_RANGE = range(128,255) # this would be 128 .. 254 inclusively # also this would require the following code added in your Accessors_site.py #define_accessors(current_module, Slice, 'VlanId', 'vlan_id', # "slice/general", "the vlan_id allocated to that slice", # get_roles=all_roles, set_roles=admin_roles) # see https://svn.planet-lab.org/wiki/TagsAndAccessors for more details class AddSlice(Method): """ Adds a new slice. Any fields specified in slice_fields are used, otherwise defaults are used. Valid slice names are lowercase and begin with the login_base (slice prefix) of a valid site, followed by a single underscore. Thereafter, only letters, numbers, or additional underscores may be used. PIs may only add slices associated with their own sites (i.e., slice prefixes must always be the login_base of one of their sites). Returns the new slice_id (> 0) if successful, faults otherwise. """ roles = ['admin', 'pi'] accepted_fields = Row.accepted_fields(can_update, Slice.fields) accepted_fields.update(Slice.tags) accepts = [ Auth(), accepted_fields ] returns = Parameter(int, 'New slice_id (> 0) if successful') def call(self, auth, slice_fields): [native,tags,rejected]=Row.split_fields(slice_fields,[Slice.fields,Slice.tags]) # type checking native = Row.check_fields (native, self.accepted_fields) if rejected: raise PLCInvalidArgument, "Cannot add Slice with column(s) %r"%rejected # Authenticated function assert self.caller is not None # 1. Lowercase. # 2. Begins with login_base (letters or numbers). # 3. Then single underscore after login_base. # 4. Then letters, numbers, or underscores. name = slice_fields['name'] good_name = r'^[a-z0-9]+_[a-zA-Z0-9_]+$' if not name or \ not re.match(good_name, name): raise PLCInvalidArgument, "Invalid slice name" # Get associated site details login_base = name.split("_")[0] sites = Sites(self.api, [login_base]) if not sites: raise PLCInvalidArgument, "Invalid slice prefix %s in %s"%(login_base,name) site = sites[0] #################### provisioning vlan_ids # retrieve all alive slices alive_slices=Slices(self.api,{},['slice_id','name','slice_tag_ids']) # build the list of their ids alive_slices_ids = [ s['slice_id'] for s in alive_slices ] # retrieve all the 'vlan_id' slice tags applying to these slice_ids vlan_slice_tags = SliceTags (self.api,{'slice_id':alive_slices_ids,'tagname':'vlan_id'}) # hash them by the slice_tag_id vlan_slice_tags_hashed_by_id = { slice_tag['slice_tag_id']:slice_tag for slice_tag in vlan_slice_tags } # build the set of allocated vlans allocated_vlan_ids = set() for alive_slice in alive_slices: # scan all slice tags attached to that slice for slice_tag_id in alive_slice['slice_tag_ids']: # discard the one that are not 'vlan_id' if slice_tag_id not in vlan_slice_tags_hashed_by_id: continue # retrive value and convert to int slice_tag = vlan_slice_tags_hashed_by_id[slice_tag_id] if slice_tag['tagname']=='vlan_id': try: allocated_vlan_ids.add(int(slice_tag['value'])) except: import traceback traceback.print_exc() pass # find a free vlan_id available=set(VLAN_RANGE) - allocated_vlan_ids try: assigned=available.pop() # piggybacking the code below that sets tags; tags value need to be strings.. tags['vlan_id']=str(assigned) except: raise Exception,"Cannot allocate slice %s, ran out of vlan_ids"%name if 'admin' not in self.caller['roles']: if site['site_id'] not in self.caller['site_ids']: raise PLCPermissionDenied, "Slice prefix %s must match one of your sites' login_base"%login_base if len(site['slice_ids']) >= site['max_slices']: raise PLCInvalidArgument, \ "Site %s has reached (%d) its maximum allowable slice count (%d)"%(site['name'], len(site['slice_ids']), site['max_slices']) if not site['enabled']: raise PLCInvalidArgument, "Site %s is disabled and can cannot create slices" % (site['name']) slice = Slice(self.api, native) slice['creator_person_id'] = self.caller['person_id'] slice['site_id'] = site['site_id'] slice.sync() # Set Slice HRN root_auth = self.api.config.PLC_HRN_ROOT tags['hrn'] = '.'.join([root_auth, login_base, name.split("_")[1]]) for (tagname,value) in tags.iteritems(): # the tagtype instance is assumed to exist, just check that if not TagTypes(self.api,{'tagname':tagname}): raise PLCInvalidArgument,"No such TagType %s"%tagname slice_tags=SliceTags(self.api,{'tagname':tagname,'slice_id':slice['slice_id']}) if not slice_tags: AddSliceTag(self.api).__call__(auth,slice['slice_id'],tagname,value) else: UpdateSliceTag(self.api).__call__(auth,slice_tags[0]['slice_tag_id'],value) # take PLC_VSYS_DEFAULTS into account for convenience try: values= [ y for y in [ x.strip() for x in self.api.config.PLC_VSYS_DEFAULTS.split(',') ] if y ] for value in values: AddSliceTag(self.api).__call__(auth,slice['slice_id'],'vsys',value) except: print "Could not set vsys tags as configured in PLC_VSYS_DEFAULTS" import traceback traceback.print_exc() self.event_objects = {'Slice': [slice['slice_id']]} self.message = "Slice %d created" % slice['slice_id'] return slice['slice_id']