@defer.inlineCallbacks
    def reserveConfirmed(self, header, connection_id, global_reservation_id, description, criteria):

        log.msg('', system=LOG_SYSTEM)
        log.msg('reserveConfirm from %s. Connection ID: %s' % (header.provider_nsa, connection_id), system=LOG_SYSTEM)

        if not header.correlation_id in self.reservations:
            msg = 'Unrecognized correlation id %s in reserveConfirmed. Connection ID %s. NSA %s' % (header.correlation_id, connection_id, header.provider_nsa)
            log.msg(msg, system=LOG_SYSTEM)
            raise error.ConnectionNonExistentError(msg)

        org_provider_nsa = self.reservations[header.correlation_id]['provider_nsa']
        if header.provider_nsa != org_provider_nsa:
            log.msg('Provider NSA in header %s for reserveConfirmed does not match saved identity %s' % (header.provider_nsa, org_provider_nsa), system=LOG_SYSTEM)
            raise error.SecurityError('Provider NSA for connection does not match saved identity')

        resv_info = self.reservations.pop(header.correlation_id)

        # gid and desc should be identical, not checking, same with bandwidth, schedule, etc

        sd = criteria.service_def
        # check that path matches our intent
        if sd.source_stp.network != resv_info['source_network']:
            log.msg('reserveConfirmed: source network mismatch (%s != %s)' % (resv_info['source_network'], sd.source_stp.network), system=LOG_SYSTEM)
        if sd.source_stp.port    != resv_info['source_port']:
            log.msg('reserveConfirmed: source port mismatch (%s != %s' % (resv_info['source_port'], sd.source_stp.port), system=LOG_SYSTEM)
        if sd.dest_stp.network   != resv_info['dest_network']:
            log.msg('reserveConfirmed: dest network mismatch', system=LOG_SYSTEM)
        if sd.dest_stp.port      != resv_info['dest_port']:
            log.msg('reserveConfirmed: dest port mismatch', system=LOG_SYSTEM)
        if not sd.source_stp.label.singleValue():
            log.msg('reserveConfirmed: source label is no a single value', system=LOG_SYSTEM)
        if not sd.dest_stp.label.singleValue():
            log.msg('reserveConfirmed: dest label is no a single value', system=LOG_SYSTEM)

        # skip label check for now
        #sd.source_stp.label.intersect(sub_connection.source_label)
        #sd.dest_stp.label.intersect(sub_connection.dest_label)

        db_start_time = criteria.schedule.start_time.isoformat() if criteria.schedule.start_time is not None else None
        db_end_time = criteria.schedule.end_time.isoformat()     if criteria.schedule.end_time   is not None else None

        # save sub connection in database
        sc = database.SubConnection(provider_nsa=org_provider_nsa, connection_id=connection_id, local_link=False, # remove local link sometime
                                    revision=criteria.revision, service_connection_id=resv_info['service_connection_id'], order_id=resv_info['order_id'],
                                    global_reservation_id=global_reservation_id, description=description,
                                    reservation_state=state.RESERVE_HELD, provision_state=state.RELEASED, lifecycle_state=state.CREATED, data_plane_active=False,
                                    source_network=sd.source_stp.network, source_port=sd.source_stp.port, source_label=sd.source_stp.label,
                                    dest_network=sd.dest_stp.network, dest_port=sd.dest_stp.port, dest_label=sd.dest_stp.label,
                                    start_time=db_start_time, end_time=db_end_time, bandwidth=sd.capacity)

        yield sc.save()

        # figure out if we can aggregate upwards

        conn = yield self.getConnectionByKey(sc.service_connection_id)
        sub_conns = yield self.getSubConnectionsByConnectionKey(conn.id)

        if sc.order_id == 0:
            conn.source_label = sd.source_stp.label
        if sc.order_id == len(sub_conns)-1:
            conn.dest_label = sd.dest_stp.label

        yield conn.save()

        outstanding_calls = [ v for v in self.reservations.values() if v.get('service_connection_id') == resv_info['service_connection_id'] ]
        if len(outstanding_calls) > 0:
            log.msg('Connection %s: Still missing %i reserveConfirmed call(s) to aggregate' % (conn.connection_id, len(outstanding_calls)), system=LOG_SYSTEM)
            return

        # if we get responses very close, multiple requests can trigger this, so we check main state as well
        if all( [ sc.reservation_state == state.RESERVE_HELD for sc in sub_conns ] ) and conn.reservation_state != state.RESERVE_HELD:
            log.msg('Connection %s: All sub connections reserve held, can emit reserveConfirmed' % (conn.connection_id), system=LOG_SYSTEM)
            yield state.reserveHeld(conn)
            header = nsa.NSIHeader(conn.requester_nsa, self.nsa_.urn())
            source_stp = nsa.STP(conn.source_network, conn.source_port, conn.source_label)
            dest_stp   = nsa.STP(conn.dest_network,   conn.dest_port,   conn.dest_label)
            schedule = nsa.Schedule(conn.start_time, conn.end_time)
            sd = nsa.Point2PointService(source_stp, dest_stp, conn.bandwidth, cnt.BIDIRECTIONAL, False, None) # we fake some thing that is not yet in the db
            conn_criteria = nsa.Criteria(conn.revision, schedule, sd)
            self.parent_requester.reserveConfirmed(header, conn.connection_id, conn.global_reservation_id, conn.description, conn_criteria)

        else:
            log.msg('Connection %s: Still missing reserveConfirmed messages before emitting to parent' % (conn.connection_id), system=LOG_SYSTEM)
dataset/ETHPy150Open NORDUnet/opennsa/opennsa/aggregator.py/Aggregator.reserveConfirmed/original