def terminate(self, header, connection_id): # return defer.fail( error.InternalNRMError('test termination failure') ) log.msg('Terminate request from %s. Connection ID: %s' % (header.requester_nsa, connection_id), system=self.log_system) conn = yield self._getConnection(connection_id, header.requester_nsa) if conn.lifecycle_state == state.TERMINATED: defer.returnValue(conn.cid) self.scheduler.cancelCall( conn.connection_id) # cancel end time tear down yield state.terminating(conn) self.logStateUpdate(conn, 'TERMINATING') yield self._doFreeResource(conn) # here the reply will practially always come before the ack header = nsa.NSIHeader( conn.requester_nsa, conn.requester_nsa ) # The NSA is both requester and provider in the backend, but this might be problematic without aggregator yield self.parent_requester.terminateConfirmed(header, conn.connection_id) yield state.terminated(conn) self.logStateUpdate(conn, 'TERMINATED')
def terminate(self, header, connection_id, request_info=None): # return defer.fail( error.InternalNRMError('test termination failure') ) log.msg('Terminate request from %s. Connection ID: %s' % (header.requester_nsa, connection_id), system=self.log_system) conn = yield self._getConnection(connection_id, header.requester_nsa) self._authorize(conn.source_port, conn.dest_port, header, request_info) if conn.lifecycle_state == state.TERMINATED: defer.returnValue(conn.cid) self.scheduler.cancelCall(conn.connection_id) # cancel end time tear down # if we passed end time, resources have already been freed free_resources = True if conn.lifecycle_state == state.PASSED_ENDTIME: free_resources = False yield state.terminating(conn) self.logStateUpdate(conn, 'TERMINATING') if free_resources: yield self._doFreeResource(conn) # here the reply will practially always come before the ack header = nsa.NSIHeader(conn.requester_nsa, conn.requester_nsa) # The NSA is both requester and provider in the backend, but this might be problematic without aggregator yield self.parent_requester.terminateConfirmed(header, conn.connection_id) yield state.terminated(conn) self.logStateUpdate(conn, 'TERMINATED')
def terminate(self, header, connection_id): log.msg('', system=LOG_SYSTEM) log.msg('Terminate request. NSA: %s. Connection ID: %s' % (header.requester_nsa, connection_id), system=LOG_SYSTEM) conn = yield self.getConnection(header.requester_nsa, connection_id) if conn.lifecycle_state == state.TERMINATED: defer.returnValue(connection_id) # all good yield state.terminating(conn) defs = [] sub_connections = yield conn.SubConnections.get() for sc in sub_connections: # we assume a provider is available provider = self.getProvider(sc.provider_nsa) header = nsa.NSIHeader(self.nsa_.urn(), sc.provider_nsa) d = provider.terminate(header, sc.connection_id) defs.append(d) results = yield defer.DeferredList(defs, consumeErrors=True) successes = [ r[0] for r in results ] if all(successes): yield state.terminated(conn) log.msg('Connection %s: Terminate succeeded' % conn.connection_id, system=LOG_SYSTEM) log.msg('Connection %s: All sub connections(%i) terminated' % (conn.connection_id, len(defs)), system=LOG_SYSTEM) else: # we are now in an inconsistent state... n_success = sum( [ 1 for s in successes if s ] ) log.msg('Connection %s. Only %i of %i connections successfully terminated' % (conn.connection_id, n_success, len(defs)), system=LOG_SYSTEM) raise _createAggregateException(connection_id, 'terminate', results, error.ConnectionError) defer.returnValue(connection_id)
def terminateConfirmed(self, header, connection_id): sub_connection = yield self.getSubConnection(header.provider_nsa, connection_id) sub_connection.reservation_state = state.TERMINATED yield sub_connection.save() conn = yield sub_connection.ServiceConnection.get() sub_conns = yield conn.SubConnections.get() if all( [ sc.reservation_state == state.TERMINATED for sc in sub_conns ] ): yield state.terminated(conn) # we always allow, even though the canonical NSI state machine does not header = nsa.NSIHeader(conn.requester_nsa, self.nsa_.urn()) self.parent_requester.terminateConfirmed(header, conn.connection_id)
def terminate(self, header, connection_id): # return defer.fail( error.InternalNRMError('test termination failure') ) conn = yield self._getConnection(connection_id, header.requester_nsa) if conn.lifecycle_state == state.TERMINATED: defer.returnValue(conn.cid) if conn.lifecycle_state == state.CREATED: yield self._doEndtime(conn) yield state.terminating(conn) self.logStateUpdate(conn, 'TERMINATING') # here the reply will practially always come before the ack header = nsa.NSIHeader(conn.requester_nsa, conn.requester_nsa) # The NSA is both requester and provider in the backend, but this might be problematic without aggregator yield self.parent_requester.terminateConfirmed(header, conn.connection_id) yield state.terminated(conn) self.logStateUpdate(conn, 'TERMINATED')
def terminate(self, header, connection_id): # return defer.fail( error.InternalNRMError('test termination failure') ) log.msg('Terminate request from %s. Connection ID: %s' % (header.requester_nsa, connection_id), system=self.log_system) conn = yield self._getConnection(connection_id, header.requester_nsa) if conn.lifecycle_state == state.TERMINATED: defer.returnValue(conn.cid) self.scheduler.cancelCall(conn.connection_id) # cancel end time tear down yield state.terminating(conn) self.logStateUpdate(conn, 'TERMINATING') yield self._doFreeResource(conn) # here the reply will practially always come before the ack header = nsa.NSIHeader(conn.requester_nsa, conn.requester_nsa) # The NSA is both requester and provider in the backend, but this might be problematic without aggregator yield self.parent_requester.terminateConfirmed(header, conn.connection_id) yield state.terminated(conn) self.logStateUpdate(conn, 'TERMINATED')
def terminate(self, header, connection_id, request_info=None): # return defer.fail( error.InternalNRMError('test termination failure') ) log.msg('Terminate request from %s. Connection ID: %s' % (header.requester_nsa, connection_id), system=self.log_system) conn = yield self._getConnection(connection_id, header.requester_nsa) self._authorize(conn.source_port, conn.dest_port, header, request_info) if conn.lifecycle_state == state.TERMINATED: defer.returnValue(conn.cid) self.scheduler.cancelCall( conn.connection_id) # cancel end time tear down # if we passed end time, resources have already been freed free_resources = True if conn.lifecycle_state == state.PASSED_ENDTIME: free_resources = False yield state.terminating(conn) self.logStateUpdate(conn, 'TERMINATING') if free_resources: yield self._doFreeResource(conn) # here the reply will practially always come before the ack header = nsa.NSIHeader( conn.requester_nsa, conn.requester_nsa ) # The NSA is both requester and provider in the backend, but this might be problematic without aggregator yield self.parent_requester.terminateConfirmed(header, conn.connection_id) yield state.terminated(conn) self.logStateUpdate(conn, 'TERMINATED')
def reserve(self, header, connection_id, global_reservation_id, description, criteria): log.msg('', system=LOG_SYSTEM) log.msg('Reserve request. NSA: %s. Connection ID: %s' % (header.requester_nsa, connection_id), system=LOG_SYSTEM) # rethink with modify if connection_id != None: connection_exists = yield database.ServiceConnection.exists(['connection_id = ?', connection_id]) if connection_exists: raise error.ConnectionExistsError('Connection with id %s already exists' % connection_id) raise NotImplementedError('Cannot handly modification of existing connections yet') connection_id = self.conn_prefix + ''.join( [ random.choice(string.hexdigits[:16]) for _ in range(12) ] ) sd = criteria.service_def source_stp = sd.source_stp dest_stp = sd.dest_stp # policy check: one endpoint must be in local network if not (source_stp.network == self.network or dest_stp.network == self.network): raise error.ConnectionCreateError('The connection does not terminate in the network, rejecting request') # check that we know the networks #self.topology.getNetwork(source_stp.network) #self.topology.getNetwork(dest_stp.network) # check that we have path vectors to topologies if self.route_vectors.vector(source_stp.network) is None: raise error.ConnectionCreateError('No know routes to network %s' % source_stp.network) if self.route_vectors.vector(dest_stp.network) is None: raise error.ConnectionCreateError('No know routes to network %s' % dest_stp.network) # if the link terminates at our network, check that ports exists if source_stp.network == self.network: self.topology.getNetwork(self.network).getPort(source_stp.port) if dest_stp.network == self.network: self.topology.getNetwork(self.network).getPort(dest_stp.port) conn = database.ServiceConnection(connection_id=connection_id, revision=0, global_reservation_id=global_reservation_id, description=description, requester_nsa=header.requester_nsa, requester_url=header.reply_to, reserve_time=datetime.datetime.utcnow(), reservation_state=state.RESERVE_START, provision_state=state.RELEASED, lifecycle_state=state.CREATED, source_network=source_stp.network, source_port=source_stp.port, source_label=source_stp.label, dest_network=dest_stp.network, dest_port=dest_stp.port, dest_label=dest_stp.label, start_time=criteria.schedule.start_time, end_time=criteria.schedule.end_time, symmetrical=sd.symmetric, directionality=sd.directionality, bandwidth=sd.capacity, connection_trace=header.connection_trace) yield conn.save() # Here we should return / callback and spawn off the path creation # Note: At his point STP Labels are candidates and they will need to be changed later # def reserveRequestsDone(results): # successes = [ r[0] for r in results ] # if all(successes): # state.reserved(conn) # log.msg('Connection %s: Reserve succeeded' % self.connection_id, system=LOG_SYSTEM) # self.scheduler.scheduleTransition(self.service_parameters.start_time, scheduled, state.RELEASED) # return self # # else: # # terminate non-failed connections # # currently we don't try and be too clever about cleaning, just do it, and switch state # defs = [] # reserved_connections = [ conn for success,conn in results if success ] # for rc in reserved_connections: # d = rc.terminate() # d.addCallbacks( # lambda c : log.msg('Succesfully terminated sub connection after partial reservation failure %s %s' % (c.curator(), connPath(c)) , system=LOG_SYSTEM), # lambda f : log.msg('Error terminating connection after partial-reservation failure: %s' % str(f), system=LOG_SYSTEM) # ) # defs.append(d) # dl = defer.DeferredList(defs) # dl.addCallback( self.state.terminatedFailed ) # # err = self._createAggregateException(results, 'reservations', error.ConnectionCreateError) # raise err yield state.reserveChecking(conn) # this also acts a lock if conn.source_network == self.network and conn.dest_network == self.network: path_info = ( conn.connection_id, self.network, conn.source_port, shortLabel(conn.source_label), conn.dest_port, shortLabel(conn.dest_label) ) log.msg('Connection %s: Local link creation: %s %s#%s -> %s#%s' % path_info, system=LOG_SYSTEM) paths = [ [ nsa.Link(self.network, conn.source_port, conn.dest_port, conn.source_label, conn.dest_label) ] ] else: # log about creation and the connection type log.msg('Connection %s: Aggregate path creation: %s -> %s' % (conn.connection_id, str(source_stp), str(dest_stp)), system=LOG_SYSTEM) # making the connection is the same for all though :-) # paths = self.topology.findPaths(source_stp, dest_stp, conn.bandwidth) # # error out if we could not find a path # if not paths: # error_msg = 'Could not find a path for route %s/%s -> %s/%s' % (source_stp.network, source_stp.port, dest_stp.network, dest_stp.port) # log.msg(error_msg, system=LOG_SYSTEM) # raise error.TopologyError(error_msg) # paths.sort(key=lambda e : len(e)) # -- vector chain path selection # how to this with path vector # 1. find topology to use from vector # 2. create abstracted path: local link + rest if source_stp.network == self.network: local_stp = source_stp remote_stp = dest_stp else: local_stp = dest_stp remote_stp = source_stp vector = self.route_vectors.vector(remote_stp.network) log.msg('Vector to %s via %s' % (remote_stp.network, vector), system=LOG_SYSTEM) ports = self.network_topology.findPorts(True) demarc_ports = [] for p in ports: if p.outbound_port.remote_port is None or p.inbound_port.remote_port is None: continue # filter out local termination ports if p.outbound_port.remote_port.startswith(remote_stp.network) and p.inbound_port.remote_port.startswith(remote_stp.network): demarc_ports.append(p) if not demarc_ports: raise error.ConnectionCreateError('Could not find a demarction port to network topology %s' % remote_stp.network) ldp = demarc_ports[0] # most of the time we will only have one anyway, should iterate and build multiple paths rd = self.topology.findDemarcationPort(ldp) if not rd: raise error.ConnectionCreateError('Could not find a demarction port for port %s' % ldp) rdn, rdp = rd paths = [ [ nsa.Link(local_stp.network, local_stp.port, ldp.id_, local_stp.label, ldp.label()), nsa.Link(remote_stp.network, rdp, remote_stp.port, ldp.label(), remote_stp.label) ] ] # the ldp label here isn't quite correct selected_path = paths[0] # shortest path log_path = ' -> '.join( [ str(p) for p in selected_path ] ) log.msg('Attempting to create path %s' % log_path, system=LOG_SYSTEM) for link in selected_path: try: self.topology.getNSA(link.network) except error.TopologyError: raise error.ConnectionCreateError('No provider for network %s. Cannot create link' % link.network) conn_info = [] for idx, link in enumerate(selected_path): provider_nsa = self.topology.getNSA(link.network) provider = self.getProvider(provider_nsa.urn()) conn_trace = header.connection_trace or [] + [ self.nsa_.urn() + ':' + conn.connection_id ] c_header = nsa.NSIHeader(self.nsa_.urn(), provider_nsa.urn(), connection_trace=conn_trace) # this has to be done more generic sometime sd = nsa.Point2PointService(nsa.STP(link.network, link.src_port, link.src_label), nsa.STP(link.network, link.dst_port, link.dst_label), conn.bandwidth, sd.directionality, sd.symmetric) # save info for db saving self.reservations[c_header.correlation_id] = { 'provider_nsa' : provider_nsa.urn(), 'service_connection_id' : conn.id, 'order_id' : idx, 'source_network' : link.network, 'source_port' : link.src_port, 'dest_network' : link.network, 'dest_port' : link.dst_port } crt = nsa.Criteria(criteria.revision, criteria.schedule, sd) d = provider.reserve(c_header, None, conn.global_reservation_id, conn.description, crt) conn_info.append( (d, provider_nsa) ) # Don't bother trying to save connection here, wait for reserveConfirmed # @defer.inlineCallbacks # def reserveResponse(connection_id, link_provider_nsa, order_id): # # need to collapse the label values when getting reserveConfirm # log.msg('Connection reservation for %s via %s acked' % (connection_id, link_provider_nsa), debug=True, system=LOG_SYSTEM) # # should probably do some sanity checks here # sp = service_params # local_link = True if link_provider_nsa == self.nsa_ else False # sc = database.SubConnection(provider_nsa=link_provider_nsa.urn(), # connection_id=connection_id, local_link=local_link, revision=0, service_connection_id=conn.id, order_id=order_id, # global_reservation_id=global_reservation_id, description=description, # reservation_state=state.RESERVE_START, provision_state=state.RELEASED, lifecycle_state=state.CREATED, data_plane_active=False, # source_network=sp.source_stp.network, source_port=sp.source_stp.port, source_label=sp.source_stp.label, # dest_network=sp.dest_stp.network, dest_port=sp.dest_stp.port, dest_label=sp.dest_stp.label, # start_time=sp.start_time.isoformat(), end_time=sp.end_time.isoformat(), bandwidth=sp.bandwidth) # yield sc.save() # defer.returnValue(sc) # # d.addCallback(reserveResponse, provider_nsa, idx) results = yield defer.DeferredList( [ c[0] for c in conn_info ], consumeErrors=True) # doesn't errback successes = [ r[0] for r in results ] if all(successes): log.msg('Connection %s: Reserve acked' % conn.connection_id, system=LOG_SYSTEM) defer.returnValue(connection_id) else: # terminate non-failed connections # currently we don't try and be too clever about cleaning, just do it, and switch state yield state.terminating(conn) defs = [] reserved_connections = [ (sc_id, provider_nsa) for (success,sc_id),(_,provider_nsa) in zip(results, conn_info) if success ] for (sc_id, provider_nsa) in reserved_connections: provider = self.getProvider(provider_nsa.urn()) t_header = nsa.NSIHeader(self.nsa_.urn(), provider_nsa.urn()) d = provider.terminate(t_header, sc_id) d.addCallbacks( lambda c : log.msg('Succesfully terminated sub connection %s at %s after partial reservation failure.' % (sc_id, provider_nsa.urn()) , system=LOG_SYSTEM), lambda f : log.msg('Error terminating connection after partial-reservation failure: %s' % str(f), system=LOG_SYSTEM) ) defs.append(d) dl = defer.DeferredList(defs) yield dl yield state.terminated(conn) err = _createAggregateException(connection_id, 'reservations', results, error.ConnectionCreateError) raise err