Esempio n. 1
0
 def setUp(self):
     self.store = Store(stamp=0.0)
     self.timer = StoreTimer(store=self.store, duration=1.0)
     self.baseDirpath = tempfile.mkdtemp(prefix="raet",  suffix="base", dir=TEMPDIR)
     self.stack = None
     # This has to be set to True in the only one process that would perform tearDown steps
     self.engine = True
     stacking.RoadStack.BurstSize = 100
Esempio n. 2
0
    def __init__(self,
                 store=None,
                 version=raeting.VERSION,
                 main=None,
                 puid=None,
                 local=None, #passed up from subclass
                 name='',
                 uid=None,
                 server=None,
                 ha=None,
                 bufcnt=2,
                 rxMsgs=None,
                 txMsgs=None,
                 rxes=None,
                 txes=None,
                 stats=None,
                ):
        '''
        Setup Stack instance
        '''
        self.store = store or Store(stamp=0.0)

        self.version = version
        self.main = main

        if getattr(self, 'puid', None) is None:
            self.puid = puid if puid is not None else self.Uid

        self.local = local or lotting.Lot(stack=self,
                                          name=name,
                                          uid=uid,
                                          ha=ha,)
        self.local.stack = self

        self.remotes = self.uidRemotes = odict() # remotes indexed by uid
        self.nameRemotes = odict() # remotes indexed by name

        self.bufcnt = bufcnt
        if not server:
            server = self.serverFromLocal()

        self.server = server
        if self.server:
            if not self.server.reopen():  # open socket
                raise raeting.StackError("Stack '{0}': Failed opening server at"
                            " '{1}'\n".format(self.name, self.server.ha))

            self.ha = self.server.ha  # update local host address after open

            console.verbose("Stack '{0}': Opened server at '{1}'\n".format(self.name,
                                                                           self.ha))

        self.rxMsgs = rxMsgs if rxMsgs is not None else deque() # messages received
        self.txMsgs = txMsgs if txMsgs is not None else deque() # messages to transmit
        self.rxes = rxes if rxes is not None else deque() # udp packets received
        self.txes = txes if txes is not None else deque() # udp packet to transmit
        self.stats = stats if stats is not None else odict() # udp statistics
        self.statTimer = StoreTimer(self.store)
Esempio n. 3
0
    def setUp(self):
        self.store = Store(stamp=0.0)
        self.timer = StoreTimer(store=self.store, duration=1.0)

        self.baseDirpath=tempfile.mkdtemp(prefix="raet",  suffix="base", dir=TEMPDIR)
        stacking.RoadStack.Bk = raeting.BodyKind.json.value

        #main stack
        mainName = "main"
        mainDirpath = os.path.join(self.baseDirpath, 'road', 'keep', mainName)
        signer = nacling.Signer()
        mainSignKeyHex = signer.keyhex
        privateer = nacling.Privateer()
        mainPriKeyHex = privateer.keyhex


        #other stack
        otherName = "other"
        otherDirpath = os.path.join(self.baseDirpath, 'road', 'keep', otherName)
        signer = nacling.Signer()
        otherSignKeyHex = signer.keyhex
        privateer = nacling.Privateer()
        otherPriKeyHex = privateer.keyhex


        keeping.clearAllKeep(mainDirpath)
        keeping.clearAllKeep(otherDirpath)

        self.main = stacking.RoadStack(store=self.store,
                                       name=mainName,
                                       main=True,
                                       auto=raeting.AutoMode.once.value,
                                       sigkey=mainSignKeyHex,
                                       prikey=mainPriKeyHex,
                                       dirpath=mainDirpath,
                                       )

        self.other = stacking.RoadStack(store=self.store,
                                        name=otherName,
                                        auto=raeting.AutoMode.once.value,
                                        ha=("", raeting.RAET_TEST_PORT),
                                        sigkey=otherSignKeyHex,
                                        prikey=otherPriKeyHex,
                                        dirpath=otherDirpath,
                                        )
Esempio n. 4
0
    def setUp(self):
        self.store = Store(stamp=0.0)
        self.timer = StoreTimer(store=self.store, duration=1.0)

        if sys.platform == 'win32':
            self.tempDirpath = tempfile.mktemp(prefix="raet",  suffix="base",
                                               dir=TEMPDIR)
        else:
            self.tempDirpath = tempfile.mkdtemp(prefix="raet",  suffix="base",
                                                dir=TEMPDIR)

        self.baseDirpath = os.path.join(self.tempDirpath, 'lane', 'keep')

        # main stack
        self.main = stacking.LaneStack(name='main',
                                       uid=1,
                                       lanename='cherry',
                                       sockdirpath=self.baseDirpath)

        #other stack
        self.other = stacking.LaneStack(name='other',
                                        uid=1,
                                        lanename='cherry',
                                        sockdirpath=self.baseDirpath)
Esempio n. 5
0
    def setUp(self):
        self.store = storing.Store(stamp=0.0)
        self.timer = StoreTimer(store=self.store, duration=1.0)

        self.saltDirpath = tempfile.mkdtemp(prefix="salt",
                                            suffix="main",
                                            dir='/tmp')

        pkiDirpath = os.path.join(self.saltDirpath, 'pki')
        if not os.path.exists(pkiDirpath):
            os.makedirs(pkiDirpath)

        acceptedDirpath = os.path.join(pkiDirpath, 'accepted')
        if not os.path.exists(acceptedDirpath):
            os.makedirs(acceptedDirpath)

        pendingDirpath = os.path.join(pkiDirpath, 'pending')
        if not os.path.exists(pendingDirpath):
            os.makedirs(pendingDirpath)

        rejectedDirpath = os.path.join(pkiDirpath, 'rejected')
        if not os.path.exists(rejectedDirpath):
            os.makedirs(rejectedDirpath)

        self.localFilepath = os.path.join(pkiDirpath, 'local.key')
        if os.path.exists(self.localFilepath):
            mode = os.stat(self.localFilepath).st_mode
            os.chmod(self.localFilepath, mode | stat.S_IWUSR | stat.S_IWUSR)

        self.cacheDirpath = os.path.join(self.saltDirpath, 'cache')
        self.sockDirpath = os.path.join(self.saltDirpath, 'sock')

        self.opts = dict(
            __role='master',
            id='master',
            pki_dir=pkiDirpath,
            sock_dir=self.sockDirpath,
            cachedir=self.cacheDirpath,
            open_mode=False,
            auto_accept=True,
            transport='raet',
        )

        self.mainKeeper = RaetKey(opts=self.opts)
        self.baseDirpath = tempfile.mkdtemp(prefix="salt",
                                            suffix="base",
                                            dir='/tmp')
Esempio n. 6
0
 def streamGet():
     """
     Create test server sent event stream that sends count events
     """
     timer = StoreTimer(store, duration=2.0)
     bottle.response.set_header('Content-Type',
                                'text/event-stream')  #text
     bottle.response.set_header('Cache-Control', 'no-cache')
     # HTTP 1.1 servers detect text/event-stream and use Transfer-Encoding: chunked
     # Set client-side auto-reconnect timeout, ms.
     yield 'retry: 1000\n\n'
     i = 0
     yield 'id: {0}\n'.format(i)
     i += 1
     yield 'data: START\n\n'
     n = 1
     while not timer.expired:
         yield 'id: {0}\n'.format(i)
         i += 1
         yield 'data: {0}\n\n'.format(n)
         n += 1
     yield "data: END\n\n"
Esempio n. 7
0
class Stack(object):
    '''
    RAET protocol base stack object.
    Should be subclassed for specific transport type such as UDP or UXD
    '''
    Count = 0
    Uid = 0  # base for next unique id for local and remotes

    def __init__(
        self,
        store=None,
        version=raeting.VERSION,
        main=None,
        puid=None,
        local=None,  #passed up from subclass
        name='',
        uid=None,
        server=None,
        ha=None,
        bufcnt=2,
        rxMsgs=None,
        txMsgs=None,
        rxes=None,
        txes=None,
        stats=None,
    ):
        '''
        Setup Stack instance
        '''
        self.store = store or Store(stamp=0.0)

        self.version = version
        self.main = main

        if getattr(self, 'puid', None) is None:
            self.puid = puid if puid is not None else self.Uid

        self.local = local or lotting.Lot(
            stack=self,
            name=name,
            uid=uid,
            ha=ha,
        )
        self.local.stack = self

        self.remotes = self.uidRemotes = odict()  # remotes indexed by uid
        self.nameRemotes = odict()  # remotes indexed by name

        self.bufcnt = bufcnt
        if not server:
            server = self.serverFromLocal()

        self.server = server
        if self.server:
            if not self.server.reopen():  # open socket
                raise raeting.StackError(
                    "Stack '{0}': Failed opening server at"
                    " '{1}'\n".format(self.name, self.server.ha))

            self.ha = self.server.ha  # update local host address after open

            console.verbose("Stack '{0}': Opened server at '{1}'\n".format(
                self.name, self.ha))

        self.rxMsgs = rxMsgs if rxMsgs is not None else deque(
        )  # messages received
        self.txMsgs = txMsgs if txMsgs is not None else deque(
        )  # messages to transmit
        self.rxes = rxes if rxes is not None else deque(
        )  # udp packets received
        self.txes = txes if txes is not None else deque(
        )  # udp packet to transmit
        self.stats = stats if stats is not None else odict()  # udp statistics
        self.statTimer = StoreTimer(self.store)

    @property
    def name(self):
        '''
        property that returns name of local interface
        '''
        return self.local.name

    @name.setter
    def name(self, value):
        '''
        setter for name property
        '''
        self.local.name = value

    @property
    def ha(self):
        '''
        property that returns host address
        '''
        return self.local.ha

    @ha.setter
    def ha(self, value):
        self.local.ha = value

    def serverFromLocal(self):
        '''
        Create server from local data
        '''
        return None

    def nextUid(self):
        '''
        Generates next unique id number for local or remotes.
        '''
        self.puid += 1
        if self.puid > long(0xffffffff):
            self.puid = 1  # rollover to 1
        return self.puid

    def addRemote(self, remote):
        '''
        Add a remote to indexes
        '''
        uid = remote.uid
        # allow for condition where local.uid == 0 and remote.uid == 0
        if uid in self.uidRemotes or (uid and uid == self.local.uid):
            emsg = "Cannot add remote at uid '{0}', alreadys exists".format(
                uid)
            raise raeting.StackError(emsg)
        if remote.name in self.nameRemotes or remote.name == self.local.name:
            emsg = "Cannot add remote at name '{0}', alreadys exists".format(
                remote.name)
            raise raeting.StackError(emsg)
        remote.stack = self
        self.uidRemotes[uid] = remote
        self.nameRemotes[remote.name] = remote
        return remote

    def moveRemote(self, remote, new):
        '''
        Move remote at key remote.uid to new uid and replace the odict key index
        so order is the same
        '''
        old = remote.uid

        if new in self.uidRemotes or new == self.local.uid:
            emsg = "Cannot move, remote to '{0}', already exists".format(new)
            raise raeting.StackError(emsg)

        if old not in self.uidRemotes:
            emsg = "Cannot move remote at '{0}', does not exist".format(old)
            raise raeting.StackError(emsg)

        if remote is not self.uidRemotes[old]:
            emsg = "Cannot move remote at '{0}', not identical".format(old)
            raise raeting.StackError(emsg)

        remote.uid = new
        index = self.uidRemotes.keys().index(old)
        del self.uidRemotes[old]
        self.uidRemotes.insert(index, new, remote)

    def renameRemote(self, remote, new):
        '''
        rename remote with old remote.name to new name but keep same index
        '''
        old = remote.name
        if new != old:
            if new in self.nameRemotes or new == self.local.name:
                emsg = "Cannot rename remote to '{0}', already exists".format(
                    new)
                raise raeting.StackError(emsg)

            if old not in self.nameRemotes:
                emsg = "Cannot rename remote '{0}', does not exist".format(old)
                raise raeting.StackError(emsg)

            if remote is not self.nameRemotes[old]:
                emsg = "Cannot rename remote '{0}', not identical".format(old)
                raise raeting.StackError(emsg)

            remote.name = new
            index = self.nameRemotes.keys().index(old)
            del self.nameRemotes[old]
            self.nameRemotes.insert(index, new, remote)

    def removeRemote(self, remote):
        '''
        Remove remote from all remotes dicts
        '''
        uid = remote.uid
        if uid not in self.uidRemotes:
            emsg = "Cannot remove remote '{0}', does not exist".format(uid)
            raise raeting.StackError(emsg)

        if remote is not self.uidRemotes[uid]:
            emsg = "Cannot remove remote '{0}', not identical".format(uid)
            raise raeting.StackError(emsg)

        del self.uidRemotes[uid]
        del self.nameRemotes[remote.name]

    def removeAllRemotes(self):
        '''
        Remove all the remotes
        '''
        remotes = self.remotes.values(
        )  #make copy since changing .remotes in-place
        for remote in remotes:
            self.removeRemote(remote)

    def fetchUidByName(self, name):
        '''
        Search for remote with matching name
        Return remote if found Otherwise return None
        '''
        remote = self.nameRemotes.get(name)
        return (remote.uid if remote else None)

    def incStat(self, key, delta=1):
        '''
        Increment stat key counter by delta
        '''
        if key in self.stats:
            self.stats[key] += delta
        else:
            self.stats[key] = delta

    def updateStat(self, key, value):
        '''
        Set stat key to value
        '''
        self.stats[key] = value

    def clearStat(self, key):
        '''
        Set the specified state counter to zero
        '''
        if key in self.stats:
            self.stats[key] = 0

    def clearStats(self):
        '''
        Set all the stat counters to zero and reset the timer
        '''
        for key, value in self.stats.items():
            self.stats[key] = 0
        self.statTimer.restart()

    def _handleOneReceived(self):
        '''
        Handle one received message from server
        assumes that there is a server
        '''
        try:
            rx, ra = self.server.receive()  # if no data the duple is ('',None)
        except socket.error as ex:
            err = raeting.get_exception_error(ex)
            if err == errno.ECONNRESET:
                return False
        if not rx:  # no received data
            return False
        self.rxes.append((rx, ra))  # duple = ( packet, source address)
        return True

    def serviceReceives(self):
        '''
        Retrieve from server all recieved and put on the rxes deque
        '''
        if self.server:
            while self._handleOneReceived():
                pass

    def serviceReceiveOnce(self):
        '''
        Retrieve from server one recieved and put on the rxes deque
        '''
        if self.server:
            self._handleOneReceived()

    def _handleOneRx(self):
        '''
        Handle on message from .rxes deque
        Assumes that there is a message on the .rxes deque
        '''
        raw, sa = self.rxes.popleft()
        console.verbose("{0} received raw message\n{1}\n".format(
            self.name, raw))
        processRx(packet=raw)

    def serviceRxes(self):
        '''
        Process all messages in .rxes deque
        '''
        while self.rxes:
            self._handleOneRx()

    def serviceRxOnce(self):
        '''
        Process one messages in .rxes deque
        '''
        if self.rxes:
            self._handleOneRx()

    def processRx(self, packet):
        '''
        Process
        '''
        pass

    def transmit(self, msg, uid=None):
        '''
        Append duple (msg, uid) to .txMsgs deque
        If msg is not mapping then raises exception
        If uid is None then it will default to the first entry in .remotes
        '''
        if not isinstance(msg, Mapping):
            emsg = "Invalid msg, not a mapping {0}\n".format(msg)
            console.terse(emsg)
            self.incStat("invalid_transmit_body")
            return
        if uid is None:
            if not self.remotes:
                emsg = "No remote to send to\n"
                console.terse(emsg)
                self.incStat("invalid_destination")
                return
            uid = self.remotes.values()[0].uid
        self.txMsgs.append((msg, uid))

    def _handleOneTxMsg(self):
        '''
        Take one message from .txMsgs deque and handle it
        Assumes there is a message on the deque
        '''
        body, uid = self.txMsgs.popleft()  # duple (body dict, destination uid
        self.message(body, uid=uid)
        console.verbose("{0} sending\n{1}\n".format(self.name, body))

    def serviceTxMsgs(self):
        '''
        Service .txMsgs queue of outgoing  messages
        '''
        while self.txMsgs:
            self._handleOneTxMsg()

    def serviceTxMsgOnce(self):
        '''
        Service one message on .txMsgs queue of outgoing messages
        '''
        if self.txMsgs:
            self._handleOneTxMsg()

    def message(self, body, uid=None):
        '''
        Sends message body to remote at uid
        '''
        pass

    def tx(self, packed, duid):
        '''
        Queue duple of (packed, da) on stack .txes queue
        Where da is the ip destination (host,port) address associated with
        the remote identified by duid
        '''
        if duid not in self.remotes:
            msg = "Invalid destination remote id '{0}'".format(duid)
            raise raeting.StackError(msg)
        self.txes.append((packed, self.remotes[duid].ha))

    def _handleOneTx(self, laters, blocks):
        '''
        Handle one message on .txes deque
        Assumes there is a message
        laters is deque of messages to try again later
        blocks is list of destinations that already blocked on this service
        '''
        tx, ta = self.txes.popleft()  # duple = (packet, destination address)

        if ta in blocks:  # already blocked on this iteration
            laters.append((tx, ta))  # keep sequential
            return

        try:
            self.server.send(tx, ta)
        except socket.error as ex:
            err = raeting.get_exception_error(ex)
            errors = [
                errno.EAGAIN, errno.EWOULDBLOCK, errno.ENETUNREACH,
                errno.EHOSTUNREACH, errno.EHOSTDOWN, errno.ECONNRESET
            ]
            if hasattr(errno, 'ETIME'):
                errors.append(errno.ETIME)
            if (err in errors):
                # problem sending such as busy with last message. save it for later
                laters.append((tx, ta))
                blocks.append(ta)
            else:
                raise

    def serviceTxes(self):
        '''
        Service the .txes deque to send  messages through server
        '''
        if self.server:
            laters = deque()
            blocks = []
            while self.txes:
                self._handleOneTx(laters, blocks)
            while laters:
                self.txes.append(laters.popleft())

    def serviceTxOnce(self):
        '''
        Service on message on the .txes deque to send through server
        '''
        if self.server:
            laters = deque()
            blocks = []  # will always be empty since only once
            if self.txes:
                self._handleOneTx(laters, blocks)
            while laters:
                self.txes.append(laters.popleft())

    def serviceAllRx(self):
        '''
        Service:
           server receive
           rxes queue
           process
        '''
        self.serviceReceives()
        self.serviceRxes()
        self.process()

    def serviceAllTx(self):
        '''
        Service:
           txMsgs queue
           txes queue to server send
        '''
        self.serviceTxMsgs()
        self.serviceTxes()

    def serviceAll(self):
        '''
        Service or Process:
           server receive
           rxes queue
           process
           txMsgs queue
           txes queue to server send
        '''
        self.serviceAllRx()
        self.serviceAllTx()

    def serviceServer(self):
        '''
        Service the server's receive and transmit queues
        '''
        self.serviceReceives()
        self.serviceTxes()

    def serviceOneAllRx(self):
        '''
        Propagate one packet all the way through the received side of the stack
        Service:
           server receive
           rxes queue
           process
        '''
        self.serviceReceiveOnce()
        self.serviceRxOnce()
        self.process()

    def serviceOneAllTx(self):
        '''
        Propagate one packet all the way through the transmit side of the stack
        Service:
           txMsgs queue
           txes queue to server send
        '''
        self.serviceTxMsgOnce()
        self.serviceTxOnce()

    def process(self):
        '''
        Allow timer based processing
        '''
        pass
Esempio n. 8
0
    def testValetServiceBottleStream(self):
        """
        Test Valet WSGI service request response stream sse
        """
        console.terse("{0}\n".format(self.testValetServiceBottleStream.__doc__))

        try:
            import bottle
        except ImportError as ex:
            console.terse("Bottle not available.\n")
            return

        store = storing.Store(stamp=0.0)

        app = bottle.default_app() # create bottle app

        @app.get('/stream')
        def streamGet():
            """
            Create test server sent event stream that sends count events
            """
            timer = StoreTimer(store, duration=2.0)
            bottle.response.set_header('Content-Type',  'text/event-stream') #text
            bottle.response.set_header('Cache-Control',  'no-cache')
            # HTTP 1.1 servers detect text/event-stream and use Transfer-Encoding: chunked
            # Set client-side auto-reconnect timeout, ms.
            yield 'retry: 1000\n\n'
            i = 0
            yield 'id: {0}\n'.format(i)
            i += 1
            yield 'data: START\n\n'
            n = 1
            while not timer.expired:
                yield 'id: {0}\n'.format(i)
                i += 1
                yield 'data: {0}\n\n'.format(n)
                n += 1
            yield "data: END\n\n"

        console.terse("{0}\n".format("Building Valet ...\n"))
        wireLogAlpha = wiring.WireLog(buffify=True, same=True)
        result = wireLogAlpha.reopen()

        alpha = serving.Valet(port = 6101,
                              bufsize=131072,
                              wlog=wireLogAlpha,
                              store=store,
                              app=app)
        self.assertIs(alpha.servant.reopen(), True)
        self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101))
        self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101))

        console.terse("{0}\n".format("Building Patron ...\n"))
        wireLogBeta = wiring.WireLog(buffify=True,  same=True)
        result = wireLogBeta.reopen()

        path = "http://{0}:{1}/".format('localhost', alpha.servant.eha[1])

        beta = clienting.Patron(bufsize=131072,
                                     wlog=wireLogBeta,
                                     store=store,
                                     path=path,
                                     reconnectable=True,
                                     )

        self.assertIs(beta.connector.reopen(), True)
        self.assertIs(beta.connector.accepted, False)
        self.assertIs(beta.connector.connected, False)
        self.assertIs(beta.connector.cutoff, False)

        request = odict([('method', u'GET'),
                         ('path', u'/stream'),
                         ('qargs', odict()),
                         ('fragment', u''),
                         ('headers', odict([('Accept', 'application/json'),
                                            ('Content-Length', 0)])),
                         ('body', None),
                        ])

        beta.requests.append(request)
        timer = StoreTimer(store, duration=1.0)
        while (not timer.expired):
            alpha.serviceAll()
            time.sleep(0.05)
            beta.serviceAll()
            time.sleep(0.05)
            store.advanceStamp(0.1)

        self.assertIs(beta.connector.accepted, True)
        self.assertIs(beta.connector.connected, True)
        self.assertIs(beta.connector.cutoff, False)

        self.assertEqual(len(alpha.servant.ixes), 1)
        self.assertEqual(len(alpha.reqs), 1)
        self.assertEqual(len(alpha.reps), 1)
        requestant = alpha.reqs.values()[0]
        self.assertEqual(requestant.method, request['method'])
        self.assertEqual(requestant.url, request['path'])
        self.assertEqual(requestant.headers, {'accept': 'application/json',
                                                'accept-encoding': 'identity',
                                                'content-length': '0',
                                                'host': 'localhost:6101'})


        #timed out while stream still open so no responses in .responses
        self.assertIs(beta.waited, True)
        self.assertIs(beta.respondent.ended, False)
        self.assertEqual(len(beta.responses), 0)
        self.assertIn('content-type', beta.respondent.headers)
        self.assertEqual(beta.respondent.headers['content-type'], 'text/event-stream')
        self.assertIn('transfer-encoding', beta.respondent.headers)
        self.assertEqual(beta.respondent.headers['transfer-encoding'], 'chunked')

        self.assertTrue(len(beta.events) >= 3)
        self.assertEqual(beta.respondent.retry, 1000)
        self.assertTrue(int(beta.respondent.leid) >= 2)
        event = beta.events.popleft()
        self.assertEqual(event, {'id': '0', 'name': '', 'data': 'START'})
        event = beta.events.popleft()
        self.assertEqual(event, {'id': '1', 'name': '', 'data': '1'})
        event = beta.events.popleft()
        self.assertEqual(event, {'id': '2', 'name': '', 'data': '2'})
        beta.events.clear()

        #keep going until ended
        timer.restart(duration=1.5)
        while (not timer.expired):
            alpha.serviceAll()
            time.sleep(0.05)
            beta.serviceAll()
            time.sleep(0.05)
            store.advanceStamp(0.1)

        self.assertTrue(len(beta.events) >= 3)
        self.assertEqual(beta.respondent.leid,  '9')
        self.assertEqual(beta.events[-2], {'id': '9', 'name': '', 'data': '9'})
        self.assertEqual(beta.events[-1], {'id': '9', 'name': '', 'data': 'END'})
        beta.events.clear()

        alpha.servant.closeAll()
        beta.connector.close()

        wireLogAlpha.close()
        wireLogBeta.close()
Esempio n. 9
0
    def testValetServiceBottleSecure(self):
        """
        Test Valet WSGI service secure TLS request response
        """
        console.terse("{0}\n".format(
            self.testValetServiceBottleSecure.__doc__))

        try:
            import bottle
        except ImportError as ex:
            console.terse("Bottle not available.\n")
            return

        store = storing.Store(stamp=0.0)

        app = bottle.default_app()  # create bottle app

        @app.get('/echo')
        @app.get('/echo/<action>')
        @app.post('/echo')
        @app.post('/echo/<action>')
        def echoGet(action=None):
            """
            Echo back request data
            """
            query = dict(bottle.request.query.items())
            body = bottle.request.json
            raw = bottle.request.body.read()
            form = odict(bottle.request.forms)

            data = odict(verb=bottle.request.method,
                         url=bottle.request.url,
                         action=action,
                         query=query,
                         form=form,
                         content=body)
            return data

        console.terse("{0}\n".format("Building Valet ...\n"))
        wireLogAlpha = wiring.WireLog(buffify=True, same=True)
        result = wireLogAlpha.reopen()

        serverCertCommonName = 'localhost'  # match hostname uses servers's cert commonname
        #serverKeypath = '/etc/pki/tls/certs/server_key.pem'  # local server private key
        #serverCertpath = '/etc/pki/tls/certs/server_cert.pem'  # local server public cert
        #clientCafilepath = '/etc/pki/tls/certs/client.pem' # remote client public cert

        serverKeypath = self.certdirpath + '/server_key.pem'  # local server private key
        serverCertpath = self.certdirpath + '/server_cert.pem'  # local server public cert
        clientCafilepath = self.certdirpath + '/client.pem'  # remote client public cert

        alpha = serving.Valet(
            port=6101,
            bufsize=131072,
            wlog=wireLogAlpha,
            store=store,
            app=app,
            scheme='https',
            keypath=serverKeypath,
            certpath=serverCertpath,
            cafilepath=clientCafilepath,
        )
        self.assertIs(alpha.servant.reopen(), True)
        self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101))
        self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101))

        console.terse("{0}\n".format("Building Patron ...\n"))
        wireLogBeta = wiring.WireLog(buffify=True, same=True)
        result = wireLogBeta.reopen()

        #clientKeypath = '/etc/pki/tls/certs/client_key.pem'  # local client private key
        #clientCertpath = '/etc/pki/tls/certs/client_cert.pem'  # local client public cert
        #serverCafilepath = '/etc/pki/tls/certs/server.pem' # remote server public cert

        clientKeypath = self.certdirpath + '/client_key.pem'  # local client private key
        clientCertpath = self.certdirpath + '/client_cert.pem'  # local client public cert
        serverCafilepath = self.certdirpath + '/server.pem'  # remote server public cert

        path = "https://{0}:{1}/".format('localhost', alpha.servant.eha[1])

        beta = clienting.Patron(bufsize=131072,
                                wlog=wireLogBeta,
                                store=store,
                                path=path,
                                reconnectable=True,
                                scheme='https',
                                certedhost=serverCertCommonName,
                                keypath=clientKeypath,
                                certpath=clientCertpath,
                                cafilepath=serverCafilepath)

        self.assertIs(beta.connector.reopen(), True)
        self.assertIs(beta.connector.accepted, False)
        self.assertIs(beta.connector.connected, False)
        self.assertIs(beta.connector.cutoff, False)

        request = odict([
            ('method', u'GET'),
            ('path', u'/echo?name=fame'),
            ('qargs', odict()),
            ('fragment', u''),
            ('headers',
             odict([('Accept', 'application/json'), ('Content-Length', 0)])),
        ])

        beta.requests.append(request)
        timer = StoreTimer(store, duration=1.0)
        while (beta.requests or beta.connector.txes or not beta.responses
               or not alpha.idle()):
            alpha.serviceAll()
            time.sleep(0.05)
            beta.serviceAll()
            time.sleep(0.05)
            store.advanceStamp(0.1)

        self.assertIs(beta.connector.accepted, True)
        self.assertIs(beta.connector.connected, True)
        self.assertIs(beta.connector.cutoff, False)

        self.assertEqual(len(alpha.servant.ixes), 1)
        self.assertEqual(len(alpha.reqs), 1)
        self.assertEqual(len(alpha.reps), 1)
        requestant = alpha.reqs.values()[0]
        self.assertEqual(requestant.method, request['method'])
        self.assertEqual(requestant.url, request['path'])
        self.assertEqual(
            requestant.headers, {
                'accept': 'application/json',
                'accept-encoding': 'identity',
                'content-length': '0',
                'host': 'localhost:6101'
            })

        self.assertEqual(len(beta.responses), 1)
        response = beta.responses.popleft()
        self.assertEqual(response['status'], 200)
        self.assertEqual(response['reason'], 'OK')
        self.assertEqual(response['body'], bytearray(b''))
        self.assertEqual(
            response['data'],
            {
                'action': None,
                'content': None,
                'form': {},
                'query': {
                    'name': 'fame'
                },
                'url': 'https://localhost:6101/echo?name=fame',
                'verb': 'GET'
            },
        )

        responder = alpha.reps.values()[0]
        self.assertTrue(responder.status.startswith, str(response['status']))
        self.assertEqual(responder.headers, response['headers'])

        alpha.servant.closeAll()
        beta.connector.close()

        wireLogAlpha.close()
        wireLogBeta.close()
Esempio n. 10
0
class BasicTestCase(unittest.TestCase):
    """"""

    def setUp(self):
        self.store = Store(stamp=0.0)
        self.timer = StoreTimer(store=self.store, duration=1.0)

        self.baseDirpath=tempfile.mkdtemp(prefix="raet",  suffix="base", dir=TEMPDIR)
        stacking.RoadStack.Bk = raeting.BodyKind.json.value

        #main stack
        mainName = "main"
        mainDirpath = os.path.join(self.baseDirpath, 'road', 'keep', mainName)
        signer = nacling.Signer()
        mainSignKeyHex = signer.keyhex
        privateer = nacling.Privateer()
        mainPriKeyHex = privateer.keyhex


        #other stack
        otherName = "other"
        otherDirpath = os.path.join(self.baseDirpath, 'road', 'keep', otherName)
        signer = nacling.Signer()
        otherSignKeyHex = signer.keyhex
        privateer = nacling.Privateer()
        otherPriKeyHex = privateer.keyhex


        keeping.clearAllKeep(mainDirpath)
        keeping.clearAllKeep(otherDirpath)

        self.main = stacking.RoadStack(store=self.store,
                                       name=mainName,
                                       main=True,
                                       auto=raeting.AutoMode.once.value,
                                       sigkey=mainSignKeyHex,
                                       prikey=mainPriKeyHex,
                                       dirpath=mainDirpath,
                                       )

        self.other = stacking.RoadStack(store=self.store,
                                        name=otherName,
                                        auto=raeting.AutoMode.once.value,
                                        ha=("", raeting.RAET_TEST_PORT),
                                        sigkey=otherSignKeyHex,
                                        prikey=otherPriKeyHex,
                                        dirpath=otherDirpath,
                                        )



    def tearDown(self):
        self.main.server.close()
        self.other.server.close()

        self.main.clearAllDir()
        self.other.clearAllDir()

        if os.path.exists(self.baseDirpath):
            shutil.rmtree(self.baseDirpath)


    def join(self, timeout=None):
        '''
        Utility method to do join. Call from test method.
        '''
        console.terse("\nJoin Transaction **************\n")
        if not self.other.remotes:
            self.other.addRemote(estating.RemoteEstate(stack=self.other,
                                                       #name=self.main.local.name,
                                                       fuid=0, # vacuous join
                                                       sid=0, # always 0 for join
                                                       ha=self.main.local.ha)
                                )
        self.other.join(timeout=timeout)
        self.service()

    def allow(self):
        '''
        Utility method to do allow. Call from test method.
        '''
        console.terse("\nAllow Transaction **************\n")
        self.other.allow()
        self.service()

    def alive(self, initiator, correspondent):
        '''
        Utility method to do alive. Call from test method.
        '''
        console.terse("\nAlive Transaction **************\n")
        initiator.alive()
        self.service()

    def service(self, duration=1.0, real=True):
        '''
        Utility method to service queues. Call from test method.
        '''
        self.timer.restart(duration=duration)
        while not self.timer.expired:
            self.other.serviceAll()
            self.main.serviceAll()
            if not (self.main.transactions or self.other.transactions):
                break
            self.store.advanceStamp(0.1)
            if real:
                time.sleep(0.1)

    def serviceOther(self, duration=1.0, real=True):
        '''
        Utility method to only service other queues. Call from test method.
        '''
        self.timer.restart(duration=duration)
        while not self.timer.expired:
            self.other.serviceAll()
            if not (self.other.transactions):
                break
            self.store.advanceStamp(0.1)
            if real:
                time.sleep(0.1)

    def serviceMain(self, duration=1.0, real=True):
        '''
        Utility method to only service main queues. Call from test method.
        '''
        self.timer.restart(duration=duration)
        while not self.timer.expired:
            self.main.serviceAll()
            if not (self.main.transactions):
                break
            self.store.advanceStamp(0.1)
            if real:
                time.sleep(0.1)

    def bootstrap(self, bk=raeting.BodyKind.json.value):
        '''
        Initialize
            main on port 7530 with uid of 1
            other on port 7531 with uid of 1
        Complete
            main joined and allowed
            other  joined and allowed
        '''
        stacking.RoadStack.Bk = bk

        self.assertEqual(self.main.name, 'main')
        self.assertEqual(self.main.local.name, 'main')
        self.assertEqual(self.main.ha, ("0.0.0.0", raeting.RAET_PORT))

        self.assertEqual(self.other.name, 'other')
        self.assertEqual(self.other.local.name, 'other')
        self.assertEqual(self.other.ha, ("0.0.0.0", raeting.RAET_TEST_PORT))

        self.join()
        console.terse("\nStack '{0}' uid= {1}\n".format(self.main.name, self.main.local.uid))
        self.assertEqual(self.main.local.uid, 1)
        self.assertEqual(self.main.name, 'main')
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.joined)
        self.assertEqual(remote.name, 'other')
        self.assertEqual(remote.uid, 2)
        self.assertEqual(remote.uid, remote.nuid)
        self.assertEqual(remote.fuid, 2)
        self.assertTrue(2 in self.main.remotes)
        self.assertTrue(remote.uid in self.main.remotes)
        self.assertTrue(len(self.main.remotes), 1)
        self.assertTrue(len(self.main.nameRemotes), 1)
        self.assertEqual(len(remote.transactions), 0)
        self.assertTrue('other' in self.main.nameRemotes)
        self.assertIs(self.main.nameRemotes[remote.name], remote)
        console.terse("Stack '{0}' estate name '{1}' joined with '{2}' = {3}\n".format(
                self.main.name, self.main.local.name, remote.name, remote.joined))

        console.terse("\nStack '{0}' uid= {1}\n".format(self.other.name, self.other.local.uid))
        self.assertEqual(self.other.local.uid, 1)
        self.assertEqual(self.other.name, 'other')
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.joined)
        self.assertEqual(remote.name, 'main')
        self.assertEqual(remote.uid, 2)
        self.assertEqual(remote.uid, remote.nuid)
        self.assertEqual(remote.fuid, 2)
        self.assertTrue(2 in self.other.remotes)
        self.assertTrue(remote.uid in self.other.remotes)
        self.assertTrue(len(self.other.remotes), 1)
        self.assertTrue(len(self.other.nameRemotes), 1)
        self.assertEqual(len(remote.transactions), 0)
        self.assertTrue('main' in self.other.nameRemotes)
        self.assertIs(self.other.nameRemotes[remote.name], remote)
        console.terse("Stack '{0}' estate name '{1}' joined with '{2}' = {3}\n".format(
                self.other.name, self.other.local.name, remote.name, remote.joined))

        self.allow()
        console.terse("\nStack '{0}' uid= {1}\n".format(self.main.name, self.main.local.uid))
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.allowed)
        self.assertEqual(len(remote.transactions), 0)
        console.terse("Stack '{0}' estate name '{1}' allowd with '{2}' = {3}\n".format(
                self.main.name, self.main.local.name, remote.name, remote.allowed))

        console.terse("\nStack '{0}' uid= {1}\n".format(self.other.name, self.other.local.uid))
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.allowed)
        self.assertEqual(len(remote.transactions), 0)
        console.terse("Stack '{0}' estate name '{1}' allowed with '{2}' = {3}\n".format(
                self.other.name, self.other.local.name, remote.name, remote.allowed))

        console.terse("\nMessage: other to main *********\n")
        body = odict(what="This is a message to the main estate. How are you", extra="I am fine.")
        self.other.txMsgs.append((body, self.other.remotes.values()[0].fuid, None))
        #self.other.message(body=body, deid=self.main.local.uid)
        self.service()

        console.terse("\nStack '{0}' uid= {1}\n".format(self.main.name, self.main.local.uid))
        self.assertEqual(len(self.main.transactions), 0)
        for msg, name in self.main.rxMsgs:
            console.terse("Estate '{0}' rxed:\n'{1}'\n".format(self.main.local.name, msg))
        self.assertDictEqual(body, self.main.rxMsgs[0][0])

        console.terse("\nMessage: main to other *********\n")
        body = odict(what="This is a message to the other estate. Get to Work", extra="Fix the fence.")
        self.main.txMsgs.append((body, self.main.remotes.values()[0].fuid, None))
        #self.main.message(body=body, deid=self.other.local.uid)
        self.service()

        console.terse("\nStack '{0}' uid= {1}\n".format(self.other.name, self.other.local.uid))
        self.assertEqual(len(self.other.transactions), 0)
        for msg, name in self.other.rxMsgs:
            console.terse("Estate '{0}' rxed:\n'{1}'\n".format(self.other.local.name, msg))
        self.assertDictEqual(body, self.other.rxMsgs[0][0])

    def bidirectional(self,
                      bk=raeting.BodyKind.json.value,
                      mains=None,
                      others=None,
                      duration=3.0,):
        '''
        Initialize
            main on port 7530 with uid of 1
            other on port 7531 with uid of 0
        Complete
            main uid of 1 joined and allowed
            other uid of 2 joined and allowed
        '''
        stacking.RoadStack.Bk = bk
        mains = mains or []
        other = others or []

        self.join()
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.joined)
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.joined)

        self.allow()
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.allowed)
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.allowed)

        console.terse("\nMessages Bidirectional *********\n")
        for msg in mains:
            self.main.transmit(msg)
        for msg in others:
            self.other.transmit(msg)

        self.service(duration=duration)

        console.terse("\nStack '{0}' uid= {1}\n".format(self.main.name, self.main.local.uid))
        self.assertEqual(len(self.main.transactions), 0)
        self.assertEqual(len(others), len(self.main.rxMsgs))
        for i, duple in enumerate(self.main.rxMsgs):
            console.terse("Estate '{0}' rxed:\n'{1}'\n".format(self.main.local.name, duple))
            self.assertDictEqual(others[i], duple[0])

        console.terse("\nStack '{0}' uid= {1}\n".format(self.other.name, self.other.local.uid))
        self.assertEqual(len(self.other.transactions), 0)
        self.assertEqual(len(mains), len(self.other.rxMsgs))
        for i, duple in enumerate(self.other.rxMsgs):
            console.terse("Estate '{0}' rxed:\n'{1}'\n".format(self.other.local.name, duple))
            self.assertDictEqual(mains[i], duple[0])

    def testBootstrapJson(self):
        '''
        Test join allow message transactions with JSON Serialization of body
        '''
        console.terse("{0}\n".format(self.testBootstrapJson.__doc__))
        self.bootstrap(bk=raeting.BodyKind.json.value)

    def testBootstrapMsgpack(self):
        '''
        Test join allow message transactions with MsgPack Serialization of body
        '''
        console.terse("{0}\n".format(self.testBootstrapMsgpack.__doc__))
        self.bootstrap(bk=raeting.BodyKind.msgpack.value)

    def testMsgBothwaysJson(self):
        '''
        Test message transactions
        '''
        console.terse("{0}\n".format(self.testMsgBothwaysJson.__doc__))

        others = []
        others.append(odict(house="Mama mia1", queue="fix me"))
        others.append(odict(house="Mama mia2", queue="help me"))
        others.append(odict(house="Mama mia3", queue="stop me"))
        others.append(odict(house="Mama mia4", queue="run me"))

        mains = []
        mains.append(odict(house="Papa pia1", queue="fix me"))
        mains.append(odict(house="Papa pia2", queue="help me"))
        mains.append(odict(house="Papa pia3", queue="stop me"))
        mains.append(odict(house="Papa pia4", queue="run me"))

        self.bidirectional(bk=raeting.BodyKind.json.value, mains=mains, others=others)

    def testMsgBothwaysMsgpack(self):
        '''
        Test message transactions
        '''
        console.terse("{0}\n".format(self.testMsgBothwaysMsgpack.__doc__))

        others = []
        others.append(odict(house="Mama mia1", queue="fix me"))
        others.append(odict(house="Mama mia2", queue="help me"))
        others.append(odict(house="Mama mia3", queue="stop me"))
        others.append(odict(house="Mama mia4", queue="run me"))

        mains = []
        mains.append(odict(house="Papa pia1", queue="fix me"))
        mains.append(odict(house="Papa pia2", queue="help me"))
        mains.append(odict(house="Papa pia3", queue="stop me"))
        mains.append(odict(house="Papa pia4", queue="run me"))

        self.bidirectional(bk=raeting.BodyKind.msgpack.value, mains=mains, others=others)

    def testMsgVeritive(self):
        '''
        Test message transactions where one is not veritive
        '''
        console.terse("{0}\n".format(self.testMsgVeritive.__doc__))

        others = []
        others.append(odict(house="Mama mia1", queue="fix me"))
        others.append(odict(house="Mama mia2", queue="help me"))
        others.append(odict(house="Mama mia3", queue="stop me"))
        others.append(odict(house="Mama mia4", queue="run me"))

        mains = []
        mains.append(odict(house="Papa pia1", queue="fix me"))
        mains.append(odict(house="Papa pia2", queue="help me"))
        mains.append(odict(house="Papa pia3", queue="stop me"))
        mains.append(odict(house="Papa pia4", queue="run me"))

        self.assertIs(self.main.veritive, True)
        self.assertIs(self.other.veritive, True)
        self.bootstrap()

        self.assertEqual(len(self.main.transactions), 0)
        self.assertEqual(len(self.main.rxMsgs), 1)
        self.main.rxMsgs.popleft()

        self.assertEqual(len(self.other.transactions), 0)
        self.assertEqual(len(self.other.rxMsgs), 1)
        self.other.rxMsgs.popleft()

        self.main.Fk = raeting.FootKind.nada.value  # main does not sign its messages

        msg = odict(house="Mama mia1", queue="fix me")
        self.main.transmit(msg)
        msg = odict(house="Papa pia1", queue="fix me")
        self.other.transmit(msg)

        self.service(duration=0.3)

        # other will reject main's message because not signed

        self.assertEqual(len(self.main.transactions), 1)
        self.assertEqual(len(self.main.rxMsgs), 1)
        self.main.rxMsgs.popleft()
        self.assertEqual(len(self.other.transactions), 0)
        self.assertEqual(len(self.other.rxMsgs), 0)

        self.assertIn('parsing_outer_error', self.other.stats)
        self.assertEqual(self.other.stats['parsing_outer_error'], 1)

        remote = self.main.remotes.values()[0]
        remote.transactions = odict()  # clear transactions
        self.assertEqual(len(self.main.transactions), 0) # no initated transaction
        self.service(duration=0.25)  # consume packet in UDP buffer

        self.other.veritive = False  # Other will accept unsigned messages
        self.assertIs(self.other.veritive, False)

        msg = odict(house="Mama mia1", queue="fix me")
        self.main.transmit(msg)
        msg = odict(house="Papa pia1", queue="fix me")
        self.other.transmit(msg)

        self.service(duration=0.3)

        # other will accept main's unsigned message but other's done ack will not
        # be accepted by main because it won't be signed and main's veritive is True
        # so mains transaction does not complete

        self.assertEqual(len(self.main.transactions), 1)
        self.assertEqual(len(self.main.rxMsgs), 1)
        self.main.rxMsgs.popleft()
        self.assertEqual(len(self.other.transactions), 0)
        self.assertEqual(len(self.other.rxMsgs), 1)
        self.other.rxMsgs.popleft()

        self.assertIn('parsing_outer_error', self.main.stats)
        self.assertEqual(self.main.stats['parsing_outer_error'], 1)

        remote = self.main.remotes.values()[0]
        remote.transactions = odict()  # clear transactions
        self.assertEqual(len(self.main.transactions), 0) # no initated transaction
        self.service(duration=0.5)  # consume packets in UDP

        self.main.veritive = False  # main will accept unsigned messages
        self.assertIs(self.main.veritive, False)

        msg = odict(house="Mama mia1", queue="fix me")
        self.main.transmit(msg)
        msg = odict(house="Papa pia1", queue="fix me")
        self.other.transmit(msg)

        self.service(duration=0.3)

        # main will now complete its transaction because it will accept unsigned acks
        # other will accept unsigned but generates signed each message completes

        self.assertEqual(len(self.main.transactions), 0)
        self.assertEqual(len(self.main.rxMsgs), 1)
        self.main.rxMsgs.popleft()
        self.assertEqual(len(self.other.transactions), 0)
        self.assertEqual(len(self.other.rxMsgs), 1)
        self.other.rxMsgs.popleft()


    def testSegmentedJson(self):
        '''
        Test segmented message transactions
        '''
        console.terse("{0}\n".format(self.testSegmentedJson.__doc__))

        stuff = []
        for i in range(300):
            stuff.append(str(i).rjust(10, " "))
        stuff = "".join(stuff)

        others = []
        mains = []
        others.append(odict(house="Snake eyes", queue="near stuff", stuff=stuff))
        mains.append(odict(house="Craps", queue="far stuff", stuff=stuff))

        bloat = []
        for i in range(300):
            bloat.append(str(i).rjust(100, " "))
        bloat = "".join(bloat)
        others.append(odict(house="Other", queue="big stuff", bloat=bloat))
        mains.append(odict(house="Main", queue="gig stuff", bloat=bloat))

        self.bidirectional(bk=raeting.BodyKind.json.value, mains=mains, others=others, duration=20.0)

    def testSegmentedMsgpack(self):
        '''
        Test segmented message transactions
        '''
        console.terse("{0}\n".format(self.testSegmentedMsgpack.__doc__))

        stuff = []
        for i in range(300):
            stuff.append(str(i).rjust(10, " "))
        stuff = "".join(stuff)

        others = []
        mains = []
        others.append(odict(house="Snake eyes", queue="near stuff", stuff=stuff))
        mains.append(odict(house="Craps", queue="far stuff", stuff=stuff))

        bloat = []
        for i in range(300):
            bloat.append(str(i).rjust(100, " "))
        bloat = "".join(bloat)
        others.append(odict(house="Other", queue="big stuff", bloat=bloat))
        mains.append(odict(house="Main", queue="gig stuff", bloat=bloat))

        self.bidirectional(bk=raeting.BodyKind.msgpack.value, mains=mains, others=others, duration=20.0)

    def testSegmentedJsonBurst(self):
        '''
        Test segmented message transactions with burst count limiting
        '''
        console.terse("{0}\n".format(self.testSegmentedJsonBurst.__doc__))

        stuff = []
        for i in range(300):
            stuff.append(str(i).rjust(10, " "))
        stuff = "".join(stuff)

        others = []
        mains = []
        others.append(odict(house="Snake eyes", queue="near stuff", stuff=stuff))
        mains.append(odict(house="Craps", queue="far stuff", stuff=stuff))

        bloat = []
        for i in range(300):
            bloat.append(str(i).rjust(100, " "))
        bloat = "".join(bloat)
        others.append(odict(house="Other", queue="big stuff", bloat=bloat))
        mains.append(odict(house="Main", queue="gig stuff", bloat=bloat))

        stacking.RoadStack.BurstSize = 16
        self.assertEqual(stacking.RoadStack.BurstSize, 16)
        self.bidirectional(bk=raeting.BodyKind.json.value, mains=mains, others=others, duration=20.0)
        stacking.RoadStack.BurstSize = 0
        self.assertEqual(stacking.RoadStack.BurstSize, 0)

    def testSegmentedMsgpackBurst(self):
        '''
        Test segmented message transactions with burst count limiting
        '''
        console.terse("{0}\n".format(self.testSegmentedMsgpackBurst.__doc__))

        stuff = []
        for i in range(300):
            stuff.append(str(i).rjust(10, " "))
        stuff = "".join(stuff)

        others = []
        mains = []
        others.append(odict(house="Snake eyes", queue="near stuff", stuff=stuff))
        mains.append(odict(house="Craps", queue="far stuff", stuff=stuff))

        bloat = []
        for i in range(300):
            bloat.append(str(i).rjust(100, " "))
        bloat = "".join(bloat)
        others.append(odict(house="Other", queue="big stuff", bloat=bloat))
        mains.append(odict(house="Main", queue="gig stuff", bloat=bloat))

        stacking.RoadStack.BurstSize = 16
        self.assertEqual(stacking.RoadStack.BurstSize, 16)
        self.bidirectional(bk=raeting.BodyKind.msgpack.value, mains=mains, others=others, duration=20.0)
        stacking.RoadStack.BurstSize = 0
        self.assertEqual(stacking.RoadStack.BurstSize, 0)

    def testJoinForever(self):
        '''
        Test other joining with timeout set to 0.0 and default
        '''
        console.terse("{0}\n".format(self.testJoinForever.__doc__))
        self.other.addRemote(estating.RemoteEstate(stack=self.other,
                                                   fuid=0, # vacuous join
                                                   sid=0, # always 0 for join
                                                   ha=self.main.local.ha))
        self.other.join(timeout=0.0) #attempt to join forever with timeout 0.0
        self.serviceOther(duration=20.0, real=False) # only service other so no response

        console.terse("\nStack '{0}' uid= {1}\n".format(self.main.name, self.main.local.uid))
        self.assertEqual(self.main.local.uid, 1)
        self.assertEqual(self.main.name, 'main')
        self.assertEqual(len(self.main.transactions), 0)
        self.assertEqual(len(self.main.remotes), 0)

        console.terse("\nStack '{0}' uid= {1}\n".format(self.other.name, self.other.local.uid))
        self.assertEqual(self.other.local.uid, 1)
        self.assertEqual(self.other.name, 'other')
        self.assertEqual(len(self.other.transactions), 1)
        remote = self.other.remotes.values()[0]
        self.assertIs(remote.joined, None)
        self.assertEqual(remote.uid, 2)
        console.terse("Stack '{0}' estate name '{1}' joined with '{2}' = {3}\n".format(
                self.other.name, self.other.local.name, remote.name, remote.joined))


        console.terse("{0} Stats\n".format(self.main.name))
        for key, val in self.main.stats.items():
            console.terse("   {0}={1}\n".format(key, val))
        self.assertEqual(len(self.main.stats), 0)

        console.terse("{0} Stats\n".format(self.other.name))
        for key, val in self.other.stats.items():
            console.terse("   {0}={1}\n".format(key, val))
        self.assertEqual(self.other.stats.get('joiner_tx_join_redo'), 6)

        # Now allow join to complete
        self.service()

        console.terse("\nStack '{0}' uid= {1}\n".format(self.main.name, self.main.local.uid))
        self.assertEqual(self.main.local.uid, 1)
        self.assertEqual(self.main.name, 'main')
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.joined)
        self.assertEqual(remote.uid, 2)
        self.assertTrue(2 in self.main.remotes)
        self.assertTrue(len(self.main.remotes), 1)
        self.assertTrue(len(self.main.nameRemotes), 1)
        self.assertEqual(remote.name, 'other')
        self.assertTrue('other' in self.main.nameRemotes)
        console.terse("Stack '{0}' estate name '{1}' joined with '{2}' = {3}\n".format(
                self.main.name, self.main.local.name, remote.name, remote.joined))

        console.terse("\nStack '{0}' uid= {1}\n".format(self.other.name, self.other.local.uid))
        self.assertEqual(self.other.local.uid, 1)
        self.assertEqual(self.other.name, 'other')
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.joined)
        self.assertEqual(remote.uid, 2)
        self.assertTrue(2 in self.other.remotes)
        self.assertTrue(len(self.other.remotes), 1)
        self.assertTrue(len(self.other.nameRemotes), 1)
        self.assertEqual(remote.name, 'main')
        self.assertTrue('main' in self.other.nameRemotes)
        console.terse("Stack '{0}' estate name '{1}' joined with '{2}' = {3}\n".format(
                self.other.name, self.other.local.name, remote.name, remote.joined))

        # Now try again with existing remote data
        self.other.join(timeout=0.0) #attempt to join forever with timeout 0.0
        self.serviceOther(duration=20.0, real=False) # only service other so no response

        # main will still have join results from previous join transaction
        console.terse("\nStack '{0}' uid= {1}\n".format(self.main.name, self.main.local.uid))
        self.assertEqual(self.main.local.uid, 1)
        self.assertEqual(self.main.name, 'main')
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.joined)
        self.assertEqual(remote.uid, 2)
        self.assertTrue(2 in self.main.remotes)
        self.assertTrue(len(self.main.remotes), 1)
        self.assertTrue(len(self.main.nameRemotes), 1)
        self.assertEqual(remote.name, 'other')
        self.assertTrue('other' in self.main.nameRemotes)
        console.terse("Stack '{0}' estate name '{1}' joined with '{2}' = {3}\n".format(
                self.main.name, self.main.local.name, remote.name, remote.joined))

        # Other will have outstanding join transaction
        console.terse("\nStack '{0}' uid= {1}\n".format(self.other.name, self.other.local.uid))
        self.assertEqual(self.other.local.uid, 1)
        self.assertEqual(self.other.name, 'other')
        self.assertEqual(len(self.other.transactions), 1)
        remote = self.other.remotes.values()[0]
        self.assertIs(remote.joined, None)
        self.assertEqual(remote.uid, 2)
        self.assertTrue(2 in self.other.remotes)
        self.assertTrue(len(self.other.remotes), 1)
        self.assertTrue(len(self.other.nameRemotes), 1)
        self.assertEqual(remote.name, 'main')
        self.assertTrue('main' in self.other.nameRemotes)
        console.terse("Stack '{0}' estate name '{1}' joined with '{2}' = {3}\n".format(
                self.other.name, self.other.local.name, remote.name, remote.joined))

        console.terse("{0} Stats\n".format(self.main.name))
        for key, val in self.main.stats.items():
            console.terse("   {0}={1}\n".format(key, val))
        self.assertEqual(self.main.stats.get('join_correspond_complete'), 1)

        console.terse("{0} Stats\n".format(self.other.name))
        for key, val in self.other.stats.items():
            console.terse("   {0}={1}\n".format(key, val))
        self.assertEqual(self.other.stats.get('joiner_tx_join_redo'), 12)
        self.assertEqual(self.other.stats.get('join_initiate_complete'), 1)

        # Now allow join to complete
        self.service()

        console.terse("\nStack '{0}' uid= {1}\n".format(self.main.name, self.main.local.uid))
        self.assertEqual(self.main.local.uid, 1)
        self.assertEqual(self.main.name, 'main')
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.joined)
        self.assertEqual(remote.uid, 2)
        self.assertTrue(2 in self.main.remotes)
        self.assertTrue(len(self.main.remotes), 1)
        self.assertTrue(len(self.main.nameRemotes), 1)
        self.assertEqual(remote.name, 'other')
        self.assertTrue('other' in self.main.nameRemotes)
        console.terse("Stack '{0}' estate name '{1}' joined with '{2}' = {3}\n".format(
                self.main.name, self.main.local.name, remote.name, remote.joined))

        console.terse("\nStack '{0}' uid= {1}\n".format(self.other.name, self.other.local.uid))
        self.assertEqual(self.other.local.uid, 1)
        self.assertEqual(self.other.name, 'other')
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.joined)
        self.assertEqual(remote.uid, 2)
        self.assertTrue(2 in self.other.remotes)
        self.assertTrue(len(self.other.remotes), 1)
        self.assertTrue(len(self.other.nameRemotes), 1)
        self.assertEqual(remote.name, 'main')
        self.assertTrue('main' in self.other.nameRemotes)
        console.terse("Stack '{0}' estate name '{1}' joined with '{2}' = {3}\n".format(
                self.other.name, self.other.local.name, remote.name, remote.joined))

    def testStaleNack(self):
        '''
        Test stale nack
        '''
        console.terse("{0}\n".format(self.testStaleNack.__doc__))

        self.join()
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.joined)
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.joined)

        self.allow()
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.allowed)
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.allowed)

        console.terse("\nMessage transaction *********\n")
        body = odict(what="This is a message to the main estate. How are you", extra="I am fine.")
        self.other.txMsgs.append((body, self.other.remotes.values()[0].fuid, None))
        self.timer.restart(duration=1.0)
        while not self.timer.expired:
            self.other.serviceAllTx() # transmit but leave receives in socket buffer
            self.main.serviceAllRx() # receive but leave transmits in queue

            self.store.advanceStamp(0.1)
            time.sleep(0.1)

        self.assertEqual(len(self.main.transactions), 0) #completed
        self.assertEqual(len(self.other.transactions), 1) # waiting for ack

        remote = self.other.remotes.values()[0]
        remote.transactions = odict() #clear transactions so RX is stale correspondent
        self.assertEqual(len(self.other.transactions), 0) # no initated transaction

        self.timer.restart(duration=2.0)
        while not self.timer.expired:
            self.main.serviceAll() # transmit stale ack
            self.other.serviceAll() # recieve ack
            self.store.advanceStamp(0.1)
            time.sleep(0.1)

        self.assertEqual(len(self.main.transactions), 0)
        self.assertEqual(len(self.other.transactions), 0)

        print("{0} Stats".format(self.main.name))
        for key, val in self.main.stats.items():
            print("   {0}={1}".format(key, val))
        print()
        print("{0} Stats".format(self.other.name))
        for key, val in self.other.stats.items():
            print("   {0}={1}".format(key, val))
        print()

        self.assertTrue(self.other.stats.get('stale_correspondent_attempt') >= 1)
        self.assertTrue(self.other.stats.get('stale_correspondent_nack') >= 1)
        self.assertTrue(self.main.stats.get('messengent_correspond_complete') >= 1)
        self.assertTrue(self.main.stats.get('stale_packet') >= 1)

    def testBasicAlive(self):
        '''
        Test basic alive transaction
        '''
        console.terse("{0}\n".format(self.testBasicAlive.__doc__))

        self.join()
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.joined)
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.joined)

        self.allow()
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.allowed)
        self.assertTrue(remote.alived)
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.allowed)
        self.assertTrue(remote.alived)

        console.terse("\nAlive Other to Main *********\n")
        otherRemote = self.main.remotes.values()[0]
        mainRemote = self.other.remotes.values()[0]
        otherRemote.alived = None
        mainRemote.alived = None

        self.alive(self.other, self.main)
        self.assertEqual(len(self.main.transactions), 0)
        self.assertTrue(otherRemote.alived)
        self.assertEqual(len(self.other.transactions), 0)
        self.assertTrue(mainRemote.alived)

        console.terse("\nAlive Main to Other *********\n")
        self.alive(self.main, self.other)
        self.assertEqual(len(self.main.transactions), 0)
        remote = self.main.remotes.values()[0]
        self.assertTrue(remote.alived)
        self.assertEqual(len(self.other.transactions), 0)
        remote = self.other.remotes.values()[0]
        self.assertTrue(remote.alived)
Esempio n. 11
0
class RemoteEstate(Estate):
    '''
    RAET protocol endpoint remote estate object ie Remote Road Lot
    Maintains verifier for verifying signatures and publican for encrypt/decrypt

    .alived attribute is the dead or alive status of the remote

    .alived = True, alive, recently have received valid signed packets from remote
    .alive = False, dead, recently have not received valid signed packets from remote

    .fuid is the far uid of the remote as owned by the farside stack
    '''
    def __init__(self,
                 stack,
                 uid=None,
                 fuid=0,
                 main=False,
                 kind=0,
                 verkey=None,
                 pubkey=None,
                 acceptance=None,
                 joined=None,
                 rsid=0,
                 **kwa):
        '''
        Setup instance

        stack is required parameter

        verkey is either nacl VerifyKey or raw or hex encoded key
        pubkey is either nacl PublicKey or raw or hex encoded key

        acceptance is accepted state of remote on Road

        rsid is last received session id used by remotely initiated transaction


        '''
        if uid is None:
            uid = stack.nextUid()
            while uid in stack.remotes or uid == stack.local.uid:
                uid = stack.nextUid()

        if 'ha' not in kwa:
            kwa['ha'] = ('127.0.0.1', raeting.RAET_TEST_PORT)
        super(RemoteEstate, self).__init__(stack, uid=uid, **kwa)
        self.fuid = fuid
        self.main = main
        self.kind = kind
        self.joined = joined
        self.allowed = None
        self.alived = None
        self.reaped = None
        self.acceptance = acceptance
        self.privee = nacling.Privateer()  # short term key manager
        self.publee = nacling.Publican(
        )  # correspondent short term key  manager
        self.verfer = nacling.Verifier(
            verkey)  # correspondent verify key manager
        self.pubber = nacling.Publican(
            pubkey)  # correspondent long term key manager

        self.rsid = rsid  # last sid received from remote when RmtFlag is True

        # persistence keep alive heartbeat timer. Initial duration has offset so
        # not synced with other side persistence heatbeet
        # by default do not use offset on main
        if self.stack.main:
            duration = self.stack.period
        else:
            duration = self.stack.period + self.stack.offset
        self.timer = StoreTimer(store=self.stack.store, duration=duration)

        self.reapTimer = StoreTimer(self.stack.store,
                                    duration=self.stack.interim)
        self.messages = deque(
        )  # deque of saved stale message body data to remote.uid

    @property
    def nuid(self):
        '''
        property that returns nuid, near uid, of remote as owned by nearside stack
        alias for uid
        '''
        return self.uid

    @nuid.setter
    def nuid(self, value):
        '''
        setter for nuid, near uid, property
        '''
        self.uid = value

    @property
    def juid(self):
        '''
        property that returns juid, join uid, duple of (nuid, fuid)
        nuid is near uid
        fuid is far uid as owned by farside stack
        '''
        return (self.nuid, self.fuid)

    @juid.setter
    def juid(self, value):
        '''
        setter for juid, join uid, property, value is duple of (nuid, fuid)
        '''
        self.nuid, self.fuid = value

    def rekey(self):
        '''
        Regenerate short term keys
        '''
        self.allowed = None
        self.privee = nacling.Privateer()  # short term key
        self.publee = nacling.Publican(
        )  # correspondent short term key  manager

    def validRsid(self, rsid):
        '''
        Compare new rsid to old .rsid and return True
        If old is zero Then new is always valid
        If new is >= old modulo N where N is 2^32 = 0x100000000
        And >= means the difference is less than N//2 = 0x80000000
        (((new - old) % 0x100000000) < (0x100000000 // 2))
        '''
        return self.validateSid(new=rsid, old=self.rsid)

    def refresh(self, alived=True):
        '''
        Restart presence heartbeat timer and conditionally reapTimer
        If alived is None then do not change .alived  but update timer
        If alived is True then set .alived to True and handle implications
        If alived is False the set .alived to False and handle implications
        '''
        self.timer.restart(duration=self.stack.period)
        if alived is None:
            return

        if self.alived or alived:  # alive before or after
            self.reapTimer.restart()
            if self.reaped:
                self.unreap()
        #otherwise let timer run both before and after are still dead
        self.alived = alived

    def manage(self, cascade=False, immediate=False):
        '''
        Perform time based processing of keep alive heatbeat
        '''
        if not self.reaped:  # only manage alives if not already reaped
            if immediate or self.timer.expired:
                # alive transaction restarts self.timer
                self.stack.alive(uid=self.uid, cascade=cascade)
            if self.stack.interim > 0.0 and self.reapTimer.expired:
                self.reap()

    def reap(self):
        '''
        Remote is dead, reap it if main estate.
        '''
        if self.stack.main:  # only main can reap
            console.concise(
                "Stack {0}: Reaping dead remote {1} at {2}\n".format(
                    self.stack.name, self.name, self.stack.store.stamp))
            self.stack.incStat("remote_reap")
            self.reaped = True

    def unreap(self):
        '''
        Remote packet received from remote so not dead anymore.
        '''
        if self.stack.main:  # only only main can reap or unreap
            console.concise(
                "Stack {0}: Unreaping dead remote {1} at {2}\n".format(
                    self.stack.name, self.name, self.stack.store.stamp))
            self.stack.incStat("remote_unreap")
            self.reaped = False

    def removeStaleCorrespondents(self):
        '''
        Remove local stale correspondent transactions associated with remote

        Local correspondent is indicated by rf ==True

        Stale means the sid in the transaction is older than the current .rsid
        assuming neither is zero, that is sid in index is older than remote.rsid

        old rsid == 0 means new always valid

        When sid in index is older than remote.rsid
        Where index is tuple: (rf, le, re, si, ti, bf,)
            rf = Remotely Initiated Flag, RmtFlag
            le = leid, Local estate ID, LEID
            re = reid, Remote estate ID, REID
            si = sid, Session ID, SID
            ti = tid, Transaction ID, TID
            bf = Broadcast Flag, BcstFlag
        '''
        for index, transaction in self.transactions.items():
            sid = index[3]
            rf = index[0]  # correspondent
            if rf and not self.validRsid(sid):
                transaction.nack()
                self.removeTransaction(index)
                emsg = (
                    "Stack {0}: Stale correspondent {1} from remote {1} at {2}"
                    "\n".format(self.stack.name, index, self.name,
                                self.stack.store.stamp))
                console.terse(emsg)
                self.stack.incStat('stale_correspondent')
        self.doneTransactions.clear()

    def replaceStaleInitiators(self):
        '''
        Save and remove any messages from messenger transactions initiated locally
        with remote

        Remove non message stale local initiator transactions associated with remote
        Also save and requeue any stale locally initiated message transactions.

        Local inititors have rf flag == False

        Stale means the sid in the transaction is older than the current .sid
        assuming neither is zero, that is  sid in index is older than remote.sid

        old sid == 0 means new always valid

        Where index is tuple: (rf, le, re, si, ti, bf,)
            rf = Remotely Initiated Flag, RmtFlag
            le = leid, Local estate ID, LEID
            re = reid, Remote estate ID, REID
            si = sid, Session ID, SID
            ti = tid, Transaction ID, TID
            bf = Broadcast Flag, BcstFlag
        '''
        for index, transaction in self.transactions.items():
            rf = index[0]
            sid = index[3]

            if not rf and not self.validSid(
                    sid):  # transaction sid newer or equal
                if transaction.kind in [TrnsKind.message]:
                    self.saveMessage(transaction)
                transaction.nack()
                self.removeTransaction(index)
                emsg = ("Stack {0}: Stale initiator {1} to remote {2} at {3}"
                        "\n".format(self.stack.name, index, self.name,
                                    self.stack.store.stamp))
                console.terse(emsg)
                self.stack.incStat('stale_initiator')

    def saveMessage(self, messenger):
        '''
        Save copy of body data from stale initiated messenger onto .messages deque
        for retransmitting later after new session is established
        messenger is instance of Messenger compatible transaction
        '''
        self.messages.append(odict(messenger.tray.body))
        emsg = ("Stack {0}: Saved stale message with remote {1}"
                "\n".format(self.stack.name, self.name))
        console.concise(emsg)

    def sendSavedMessages(self):
        '''
        Message is Messenger compatible transaction
        Save stale initiated message for retransmitting later after new session is established
        '''
        while self.messages:
            body = self.messages.popleft()
            self.stack.message(body, uid=self.uid)
            emsg = ("Stack {0}: Resent saved message with remote {1}"
                    "\n".format(self.stack.name, self.name))
            console.concise(emsg)

    def allowInProcess(self):
        '''
        Returns list of transactions for all allow transactions with this remote
        that are already in process
        '''
        return ([
            t for t in self.transactions.values() if t.kind == TrnsKind.allow
        ])

    def joinInProcess(self):
        '''
        Returns  list of transactions for all join transaction with this remote
        that are already in process
        '''
        return ([
            t for t in self.transactions.values() if t.kind == TrnsKind.join
        ])
Esempio n. 12
0
class Stack(object):
    '''
    RAET protocol base stack object.
    Should be subclassed for specific transport type such as UDP or UXD
    '''
    Count = 0
    Uid = 0 # base for next unique id for local and remotes

    def __init__(self,
                 store=None,
                 version=raeting.VERSION,
                 main=None,
                 puid=None,
                 local=None, #passed up from subclass
                 name='',
                 uid=None,
                 server=None,
                 ha=None,
                 bufcnt=2,
                 rxMsgs=None,
                 txMsgs=None,
                 rxes=None,
                 txes=None,
                 stats=None,
                ):
        '''
        Setup Stack instance
        '''
        self.store = store or Store(stamp=0.0)

        self.version = version
        self.main = main

        if getattr(self, 'puid', None) is None:
            self.puid = puid if puid is not None else self.Uid

        self.local = local or lotting.Lot(stack=self,
                                          name=name,
                                          uid=uid,
                                          ha=ha,)
        self.local.stack = self

        self.remotes = self.uidRemotes = odict() # remotes indexed by uid
        self.nameRemotes = odict() # remotes indexed by name

        self.bufcnt = bufcnt
        if not server:
            server = self.serverFromLocal()

        self.server = server
        if self.server:
            if not self.server.reopen():  # open socket
                raise raeting.StackError("Stack '{0}': Failed opening server at"
                            " '{1}'\n".format(self.name, self.server.ha))

            self.ha = self.server.ha  # update local host address after open

            console.verbose("Stack '{0}': Opened server at '{1}'\n".format(self.name,
                                                                           self.ha))

        self.rxMsgs = rxMsgs if rxMsgs is not None else deque() # messages received
        self.txMsgs = txMsgs if txMsgs is not None else deque() # messages to transmit
        self.rxes = rxes if rxes is not None else deque() # udp packets received
        self.txes = txes if txes is not None else deque() # udp packet to transmit
        self.stats = stats if stats is not None else odict() # udp statistics
        self.statTimer = StoreTimer(self.store)

    @property
    def name(self):
        '''
        property that returns name of local interface
        '''
        return self.local.name

    @name.setter
    def name(self, value):
        '''
        setter for name property
        '''
        self.local.name = value

    @property
    def ha(self):
        '''
        property that returns host address
        '''
        return self.local.ha

    @ha.setter
    def ha(self, value):
        self.local.ha = value

    def serverFromLocal(self):
        '''
        Create server from local data
        '''
        return None

    def nextUid(self):
        '''
        Generates next unique id number for local or remotes.
        '''
        self.puid += 1
        if self.puid > long(0xffffffff):
            self.puid = 1  # rollover to 1
        return self.puid

    def addRemote(self, remote):
        '''
        Add a remote to indexes
        '''
        uid = remote.uid
        # allow for condition where local.uid == 0 and remote.uid == 0
        if uid in self.uidRemotes or (uid and uid == self.local.uid):
            emsg = "Cannot add remote at uid '{0}', alreadys exists".format(uid)
            raise raeting.StackError(emsg)
        if remote.name in  self.nameRemotes or remote.name == self.local.name:
            emsg = "Cannot add remote at name '{0}', alreadys exists".format(remote.name)
            raise raeting.StackError(emsg)
        remote.stack = self
        self.uidRemotes[uid] = remote
        self.nameRemotes[remote.name] = remote
        return remote

    def moveRemote(self, remote, new):
        '''
        Move remote at key remote.uid to new uid and replace the odict key index
        so order is the same
        '''
        old = remote.uid

        if new in self.uidRemotes or new == self.local.uid:
            emsg = "Cannot move, remote to '{0}', already exists".format(new)
            raise raeting.StackError(emsg)

        if old not in self.uidRemotes:
            emsg = "Cannot move remote at '{0}', does not exist".format(old)
            raise raeting.StackError(emsg)

        if remote is not self.uidRemotes[old]:
            emsg = "Cannot move remote at '{0}', not identical".format(old)
            raise raeting.StackError(emsg)

        remote.uid = new
        index = self.uidRemotes.keys().index(old)
        del self.uidRemotes[old]
        self.uidRemotes.insert(index, new, remote)

    def renameRemote(self, remote, new):
        '''
        rename remote with old remote.name to new name but keep same index
        '''
        old = remote.name
        if new != old:
            if new in self.nameRemotes or new == self.local.name:
                emsg = "Cannot rename remote to '{0}', already exists".format(new)
                raise raeting.StackError(emsg)

            if old not in self.nameRemotes:
                emsg = "Cannot rename remote '{0}', does not exist".format(old)
                raise raeting.StackError(emsg)

            if remote is not self.nameRemotes[old]:
                emsg = "Cannot rename remote '{0}', not identical".format(old)
                raise raeting.StackError(emsg)

            remote.name = new
            index = self.nameRemotes.keys().index(old)
            del self.nameRemotes[old]
            self.nameRemotes.insert(index, new, remote)

    def removeRemote(self, remote):
        '''
        Remove remote from all remotes dicts
        '''
        uid = remote.uid
        if uid not in self.uidRemotes:
            emsg = "Cannot remove remote '{0}', does not exist".format(uid)
            raise raeting.StackError(emsg)

        if remote is not self.uidRemotes[uid]:
            emsg = "Cannot remove remote '{0}', not identical".format(uid)
            raise raeting.StackError(emsg)

        del self.uidRemotes[uid]
        del self.nameRemotes[remote.name]

    def removeAllRemotes(self):
        '''
        Remove all the remotes
        '''
        remotes = self.remotes.values() #make copy since changing .remotes in-place
        for remote in remotes:
            self.removeRemote(remote)

    def fetchUidByName(self, name):
        '''
        Search for remote with matching name
        Return remote if found Otherwise return None
        '''
        remote = self.nameRemotes.get(name)
        return (remote.uid if remote else None)

    def incStat(self, key, delta=1):
        '''
        Increment stat key counter by delta
        '''
        if key in self.stats:
            self.stats[key] += delta
        else:
            self.stats[key] = delta

    def updateStat(self, key, value):
        '''
        Set stat key to value
        '''
        self.stats[key] = value

    def clearStat(self, key):
        '''
        Set the specified state counter to zero
        '''
        if key in self.stats:
            self.stats[key] = 0

    def clearStats(self):
        '''
        Set all the stat counters to zero and reset the timer
        '''
        for key, value in self.stats.items():
            self.stats[key] = 0
        self.statTimer.restart()

    def _handleOneReceived(self):
        '''
        Handle one received message from server
        assumes that there is a server
        '''
        try:
            rx, ra = self.server.receive()  # if no data the duple is ('',None)
        except socket.error as ex:
            err = raeting.get_exception_error(ex)
            if err == errno.ECONNRESET:
                return False
        if not rx:  # no received data
            return False
        self.rxes.append((rx, ra))     # duple = ( packet, source address)
        return True

    def serviceReceives(self):
        '''
        Retrieve from server all recieved and put on the rxes deque
        '''
        if self.server:
            while self._handleOneReceived():
                pass

    def serviceReceiveOnce(self):
        '''
        Retrieve from server one recieved and put on the rxes deque
        '''
        if self.server:
            self._handleOneReceived()

    def _handleOneRx(self):
        '''
        Handle on message from .rxes deque
        Assumes that there is a message on the .rxes deque
        '''
        raw, sa = self.rxes.popleft()
        console.verbose("{0} received raw message\n{1}\n".format(self.name, raw))
        processRx(packet=raw)

    def serviceRxes(self):
        '''
        Process all messages in .rxes deque
        '''
        while self.rxes:
            self._handleOneRx()

    def serviceRxOnce(self):
        '''
        Process one messages in .rxes deque
        '''
        if self.rxes:
            self._handleOneRx()

    def processRx(self, packet):
        '''
        Process
        '''
        pass

    def transmit(self, msg, uid=None):
        '''
        Append duple (msg, uid) to .txMsgs deque
        If msg is not mapping then raises exception
        If uid is None then it will default to the first entry in .remotes
        '''
        if not isinstance(msg, Mapping):
            emsg = "Invalid msg, not a mapping {0}\n".format(msg)
            console.terse(emsg)
            self.incStat("invalid_transmit_body")
            return
        if uid is None:
            if not self.remotes:
                emsg = "No remote to send to\n"
                console.terse(emsg)
                self.incStat("invalid_destination")
                return
            uid = self.remotes.values()[0].uid
        self.txMsgs.append((msg, uid))

    def  _handleOneTxMsg(self):
        '''
        Take one message from .txMsgs deque and handle it
        Assumes there is a message on the deque
        '''
        body, uid = self.txMsgs.popleft() # duple (body dict, destination uid
        self.message(body, uid=uid)
        console.verbose("{0} sending\n{1}\n".format(self.name, body))

    def serviceTxMsgs(self):
        '''
        Service .txMsgs queue of outgoing  messages
        '''
        while self.txMsgs:
            self._handleOneTxMsg()

    def serviceTxMsgOnce(self):
        '''
        Service one message on .txMsgs queue of outgoing messages
        '''
        if self.txMsgs:
            self._handleOneTxMsg()

    def message(self, body, uid=None):
        '''
        Sends message body to remote at uid
        '''
        pass

    def tx(self, packed, duid):
        '''
        Queue duple of (packed, da) on stack .txes queue
        Where da is the ip destination (host,port) address associated with
        the remote identified by duid
        '''
        if duid not in self.remotes:
            msg = "Invalid destination remote id '{0}'".format(duid)
            raise raeting.StackError(msg)
        self.txes.append((packed, self.remotes[duid].ha))

    def _handleOneTx(self, laters, blocks):
        '''
        Handle one message on .txes deque
        Assumes there is a message
        laters is deque of messages to try again later
        blocks is list of destinations that already blocked on this service
        '''
        tx, ta = self.txes.popleft()  # duple = (packet, destination address)

        if ta in blocks: # already blocked on this iteration
            laters.append((tx, ta)) # keep sequential
            return

        try:
            self.server.send(tx, ta)
        except socket.error as ex:
            err = raeting.get_exception_error(ex)
            errors = [errno.EAGAIN,
                      errno.EWOULDBLOCK,
                      errno.ENETUNREACH,
                      errno.EHOSTUNREACH,
                      errno.EHOSTDOWN,
                      errno.ECONNRESET]
            if hasattr(errno, 'ETIME'):
                errors.append[errno.ETIME]
            if (err in errors):
                # problem sending such as busy with last message. save it for later
                laters.append((tx, ta))
                blocks.append(ta)
            else:
                raise

    def serviceTxes(self):
        '''
        Service the .txes deque to send  messages through server
        '''
        if self.server:
            laters = deque()
            blocks = []
            while self.txes:
                self._handleOneTx(laters, blocks)
            while laters:
                self.txes.append(laters.popleft())

    def serviceTxOnce(self):
        '''
        Service on message on the .txes deque to send through server
        '''
        if self.server:
            laters = deque()
            blocks = [] # will always be empty since only once
            if self.txes:
                self._handleOneTx(laters, blocks)
            while laters:
                self.txes.append(laters.popleft())

    def serviceAllRx(self):
        '''
        Service:
           server receive
           rxes queue
           process
        '''
        self.serviceReceives()
        self.serviceRxes()
        self.process()

    def serviceAllTx(self):
        '''
        Service:
           txMsgs queue
           txes queue to server send
        '''
        self.serviceTxMsgs()
        self.serviceTxes()

    def serviceAll(self):
        '''
        Service or Process:
           server receive
           rxes queue
           process
           txMsgs queue
           txes queue to server send
        '''
        self.serviceAllRx()
        self.serviceAllTx()

    def serviceServer(self):
        '''
        Service the server's receive and transmit queues
        '''
        self.serviceReceives()
        self.serviceTxes()

    def serviceOneAllRx(self):
        '''
        Propagate one packet all the way through the received side of the stack
        Service:
           server receive
           rxes queue
           process
        '''
        self.serviceReceiveOnce()
        self.serviceRxOnce()
        self.process()

    def serviceOneAllTx(self):
        '''
        Propagate one packet all the way through the transmit side of the stack
        Service:
           txMsgs queue
           txes queue to server send
        '''
        self.serviceTxMsgOnce()
        self.serviceTxOnce()

    def process(self):
        '''
        Allow timer based processing
        '''
        pass
Esempio n. 13
0
 def setUp(self):
     self.store = storing.Store(stamp=0.0)
     self.timer = StoreTimer(store=self.store, duration=1.0)
     self.port = 4506
     self.opts = dict(master_port=self.port)
Esempio n. 14
0
    def setUp(self):
        self.store = Store(stamp=0.0)
        self.timer = StoreTimer(store=self.store, duration=1.0)

        self.dirpathBase = tempfile.mkdtemp(prefix="raet",
                                            suffix="base",
                                            dir=TEMPDIR)
        stacking.RoadStack.Bk = raeting.BodyKind.json.value

        #main stack
        mainName = "main"
        mainDirpath = os.path.join(self.dirpathBase, 'road', 'keep', mainName)
        signer = nacling.Signer()
        mainSignKeyHex = signer.keyhex
        mainVerKeyHex = signer.verhex
        privateer = nacling.Privateer()
        mainPriKeyHex = privateer.keyhex
        mainPubKeyHex = privateer.pubhex

        #other stack
        otherName = "other"
        otherDirpath = os.path.join(self.dirpathBase, 'road', 'keep',
                                    otherName)
        signer = nacling.Signer()
        otherSignKeyHex = signer.keyhex
        otherVerKeyHex = signer.verhex
        privateer = nacling.Privateer()
        otherPriKeyHex = privateer.keyhex
        otherPubKeyHex = privateer.pubhex

        keeping.clearAllKeep(mainDirpath)
        keeping.clearAllKeep(otherDirpath)

        self.main = stacking.RoadStack(name=mainName,
                                       uid=1,
                                       sigkey=mainSignKeyHex,
                                       prikey=mainPriKeyHex,
                                       auto=raeting.AutoMode.once.value,
                                       main=True,
                                       dirpath=mainDirpath,
                                       store=self.store)

        remote1 = estating.RemoteEstate(
            stack=self.main,
            uid=2,
            name=otherName,
            ha=("127.0.0.1", raeting.RAET_TEST_PORT),
            verkey=otherVerKeyHex,
            pubkey=otherPubKeyHex,
        )
        self.main.addRemote(remote1)

        self.other = stacking.RoadStack(name=otherName,
                                        uid=2,
                                        auto=raeting.AutoMode.once.value,
                                        ha=("", raeting.RAET_TEST_PORT),
                                        sigkey=otherSignKeyHex,
                                        prikey=otherPriKeyHex,
                                        dirpath=otherDirpath,
                                        store=self.store)

        remote0 = estating.RemoteEstate(
            stack=self.other,
            uid=3,
            name=mainName,
            ha=('127.0.0.1', raeting.RAET_PORT),
            verkey=mainVerKeyHex,
            pubkey=mainPubKeyHex,
        )
        self.other.addRemote(remote0)

        remote0.publee = nacling.Publican(key=remote1.privee.pubhex)
        remote1.publee = nacling.Publican(key=remote0.privee.pubhex)

        stuff = []
        for i in range(300):
            stuff.append(str(i).rjust(4, " "))
        self.stuff = ns2b("".join(stuff))

        self.data = odict(hk=raeting.HeadKind.raet.value)
Esempio n. 15
0
def test_get_backend():
    """
    """
    print("Testing Falcon Example Backend Call")

    store = Store(stamp=0.0)  # create store
    priming.setupTest()

    valet = Valet(
        port=8101,
        bufsize=131072,
        store=store,
        app=exapp,
    )

    result = valet.open()
    assert result
    assert valet.servant.ha == ('0.0.0.0', 8101)
    assert valet.servant.eha == ('127.0.0.1', 8101)

    path = "http://{}:{}{}".format('localhost', valet.servant.eha[1],
                                   "/example/backend")
    headers = odict([('Accept', 'application/json'), ('Content-Length', 0)])
    patron = Patron(
        bufsize=131072,
        store=store,
        method='GET',
        path=path,
        headers=headers,
        reconnectable=True,
    )

    assert patron.connector.reopen()
    assert patron.connector.accepted == False
    assert patron.connector.connected == False
    assert patron.connector.cutoff == False

    patron.transmit()
    timer = StoreTimer(store, duration=1.0)
    while (patron.requests or patron.connector.txes or not patron.responses
           or not valet.idle()):
        valet.serviceAll()
        time.sleep(0.05)
        patron.serviceAll()
        time.sleep(0.05)
        store.advanceStamp(0.1)

    assert patron.connector.accepted == True
    assert patron.connector.connected == True
    assert patron.connector.cutoff == False

    assert len(valet.servant.ixes) == 1
    assert len(valet.reqs) == 1
    assert len(valet.reps) == 1
    requestant = valet.reqs.values()[0]
    assert requestant.method == patron.requester.method
    assert requestant.url == patron.requester.path
    assert requestant.headers == {
        'accept': 'application/json',
        'accept-encoding': 'identity',
        'content-length': '0',
        'host': 'localhost:8101'
    }

    assert len(patron.responses) == 1
    rep = patron.responses.popleft()
    assert rep['status'] == 200
    assert rep['reason'] == 'OK'
    assert rep['body'] == bytearray(
        b'{\n  "approved": true,\n  "body": "\\nHello World\\n\\n"\n}')
    assert rep['data'] == odict([('approved', True),
                                 ('body', '\nHello World\n\n')])

    responder = valet.reps.values()[0]
    assert responder.status.startswith(str(rep['status']))
    assert responder.headers == rep['headers']

    # test for error by sending query arg path
    #request = odict([('method', 'GET'),
    #('path', '/example/backend'),
    #('qargs', odict(path='/unknown')),
    #('fragment', u''),
    #('headers', odict([('Accept', 'application/json'),
    #('Content-Length', 0)])),
    #])

    #patron.requests.append(request)

    headers = odict([('Accept', 'application/json'), ('Content-Length', 0)])
    patron.request(method='GET',
                   path='/example/backend',
                   qargs=odict(path='/unknown'),
                   headers=headers)
    timer = StoreTimer(store, duration=1.0)
    while (patron.requests or patron.connector.txes or not patron.responses
           or not valet.idle()):
        valet.serviceAll()
        time.sleep(0.05)
        patron.serviceAll()
        time.sleep(0.05)
        store.advanceStamp(0.1)

    assert len(patron.responses) == 1
    rep = patron.responses.popleft()
    assert rep['status'] == 404
    assert rep['reason'] == 'Not Found'
    assert rep['body'] == bytearray(b'404 Not Found\nBackend Validation'
                                    b' Error\nError backend validation.'
                                    b' unknown\n')
    assert not rep['data']

    valet.close()
    patron.close()
    print("Done Test")
Esempio n. 16
0
class BasicTestCase(unittest.TestCase):
    """"""

    def setUp(self):
        self.store = Store(stamp=0.0)
        self.timer = StoreTimer(store=self.store, duration=1.0)

        if sys.platform == 'win32':
            self.tempDirpath = tempfile.mktemp(prefix="raet",  suffix="base",
                                               dir=TEMPDIR)
        else:
            self.tempDirpath = tempfile.mkdtemp(prefix="raet",  suffix="base",
                                                dir=TEMPDIR)

        self.baseDirpath = os.path.join(self.tempDirpath, 'lane', 'keep')

        # main stack
        self.main = stacking.LaneStack(name='main',
                                       uid=1,
                                       lanename='cherry',
                                       sockdirpath=self.baseDirpath)

        #other stack
        self.other = stacking.LaneStack(name='other',
                                        uid=1,
                                        lanename='cherry',
                                        sockdirpath=self.baseDirpath)

    def tearDown(self):
        self.main.server.close()
        self.other.server.close()

        if not sys.platform == 'win32':
            shutil.rmtree(self.tempDirpath)

    def service(self, duration=1.0, real=True):
        '''
        Utility method to service queues. Call from test method.
        '''
        self.timer.restart(duration=duration)
        while not self.timer.expired:
            self.other.serviceAll()
            self.main.serviceAll()
            self.store.advanceStamp(0.1)
            if real:
                time.sleep(0.1)

    def serviceOther(self, duration=1.0, real=True):
        '''
        Utility method to only service other queues. Call from test method.
        '''
        self.timer.restart(duration=duration)
        while not self.timer.expired:
            self.other.serviceAll()
            self.store.advanceStamp(0.1)
            if real:
                time.sleep(0.1)

    def serviceMain(self, duration=1.0, real=True):
        '''
        Utility method to only service main queues. Call from test method.
        '''
        self.timer.restart(duration=duration)
        while not self.timer.expired:
            self.main.serviceAll()
            self.store.advanceStamp(0.1)
            if real:
                time.sleep(0.1)

    def bootstrap(self, kind=raeting.PackKind.json.value):
        '''
        Basic messaging
        '''
        self.main.addRemote(yarding.RemoteYard(stack=self.main, ha=self.other.ha))
        self.other.addRemote(yarding.RemoteYard(stack=self.other, ha=self.main.ha))

        self.assertEqual(self.main.name, 'main')
        self.assertEqual(self.main.local.name, 'main')
        self.assertEqual(self.main.ha, os.path.join(self.baseDirpath, 'cherry.main.uxd'))
        self.assertEqual(len(self.main.remotes), 1)
        remote = self.main.remotes.values()[0]
        self.assertEqual(remote.ha, os.path.join(self.baseDirpath, 'cherry.other.uxd'))
        self.assertEqual(remote.name, 'other')
        self.assertTrue(remote.uid in self.main.remotes)
        self.assertTrue(remote.name in self.main.nameRemotes)
        self.assertTrue(remote.ha in self.main.haRemotes)
        self.assertIs(self.main.nameRemotes[remote.name], remote)
        self.assertIs(self.main.haRemotes[remote.ha], remote)


        self.assertEqual(self.other.name, 'other')
        self.assertEqual(self.other.local.name, 'other')
        self.assertEqual(self.other.ha, os.path.join(self.baseDirpath, 'cherry.other.uxd'))
        self.assertEqual(len(self.other.remotes), 1)
        remote = self.other.remotes.values()[0]
        self.assertEqual(remote.ha, os.path.join(self.baseDirpath, 'cherry.main.uxd'))
        self.assertEqual(remote.name, 'main')
        self.assertTrue(remote.uid in self.other.remotes)
        self.assertTrue(remote.name in self.other.nameRemotes)
        self.assertTrue(remote.ha in self.other.haRemotes)
        self.assertIs(self.other.nameRemotes[remote.name], remote)
        self.assertIs(self.other.haRemotes[remote.ha], remote)

        stacking.LaneStack.Pk = kind

    def message(self, mains, others, duration=1.0):
        '''
        Transmit and receive messages in mains and others lists
        '''
        for msg in mains:
            self.main.transmit(msg=msg)

        for msg in others:
            self.other.transmit(msg=msg)

        self.service(duration=duration)

        self.assertEqual(len(self.main.rxMsgs), len(others))
        for i, duple in enumerate(self.main.rxMsgs):
            console.terse("Yard '{0}' rxed:\n'{1}'\n".format(self.main.local.name, duple))
            self.assertDictEqual(others[i], duple[0])

        self.assertEqual(len(self.other.rxMsgs), len(mains))
        for i, duple in enumerate(self.other.rxMsgs):
            console.terse("Yard '{0}' rxed:\n'{1}'\n".format(self.other.local.name, duple))
            self.assertDictEqual(mains[i], duple[0])

    def createLaneData(self, name, uid, base, lanename):
        '''
        Creates odict and populates with data to setup lane stack
        {
            name: stack name
            dirpath: dirpath for keep files
            lanename: name of yard
        }
        '''
        data = odict()
        data['name'] = name
        data['uid'] = uid
        data['sockdirpath'] = os.path.join(base, name)
        data['lanename'] = lanename

        return data

    def createLaneStack(self, data, main=None):
        '''
        Creates stack and local yard from data
        returns stack

        '''
        stack = stacking.LaneStack(name=data['name'],
                                   uid=data['uid'],
                                   main=main,
                                   lanename=data['lanename'],
                                   sockdirpath=data['sockdirpath'])

        return stack

    def serviceMainOther(self, main, other, duration=1.0):
        '''
        Utility method to service queues. Call from test method.
        '''
        self.timer.restart(duration=duration)
        while not self.timer.expired:
            other.serviceAll()
            main.serviceAll()
            self.store.advanceStamp(0.1)
            time.sleep(0.1)


    def messageMainOther(self, main,  other, mains, others, duration=1.0):
        '''
        Utility to send messages both ways
        '''
        for msg in mains:
            main.transmit(msg, uid=main.fetchUidByName(other.local.name))
        for msg in others:
            other.transmit(msg,  uid=other.fetchUidByName(main.local.name))

        self.serviceMainOther(main, other, duration=duration)

        self.assertEqual(len(main.rxMsgs), len(others))
        for i, duple in enumerate(main.rxMsgs):
            console.terse("Yard '{0}' rxed:\n'{1}'\n".format(main.local.name, duple))
            self.assertDictEqual(others[i], duple[0])

        self.assertEqual(len(other.rxMsgs), len(mains))
        for i, duple in enumerate(other.rxMsgs):
            console.terse("Yard '{0}' rxed:\n'{1}'\n".format(other.local.name, duple))
            self.assertDictEqual(mains[i], duple[0])

    def serviceStackOneTx(self, stack):
        '''
        Utility method to service one packet on Tx queues. Call from test method.
        '''
        stack.serviceOneAllTx()
        time.sleep(0.1)
        self.store.advanceStamp(0.1)

    def serviceStackOneRx(self, stack):
        '''
        Utility method to service one packet on Rx queues. Call from test method.
        '''
        stack.serviceOneAllRx()
        time.sleep(0.1)
        self.store.advanceStamp(0.1)

    def serviceOneTx(self, main, other):
        '''
        Utility method to service one packet on Tx queues. Call from test method.
        '''
        other.serviceOneAllTx()
        main.serviceOneAllTx()
        time.sleep(0.1)
        self.store.advanceStamp(0.1)

    def serviceOneRx(self, main, other):
        '''
        Utility method to service one packet on Rx queues. Call from test method.
        '''
        other.serviceOneAllRx()
        main.serviceOneAllRx()
        time.sleep(0.1)
        self.store.advanceStamp(0.1)

    def serviceOneAll(self, main, other):
        '''
        Utility method to service one packet on all queues. Call from test method.
        '''
        self.serviceOneTx(main=main, other=other)
        self.serviceOneRx(main=main, other=other)


    def serviceStack(self, stack, duration=1.0):
        '''
        Utility method to service queues for one stack. Call from test method.
        '''
        self.timer.restart(duration=duration)
        while not self.timer.expired:
            stack.serviceAll()
            self.store.advanceStamp(0.1)
            time.sleep(0.1)

    def serviceStacks(self, stacks, duration=1.0):
        '''
        Utility method to service queues for list of stacks. Call from test method.
        '''
        self.timer.restart(duration=duration)
        while not self.timer.expired:
            for stack in stacks:
                stack.serviceAll()
            self.store.advanceStamp(0.1)
            time.sleep(0.1)

    def testMessageJson(self):
        '''
        Basic messaging with json packing
        '''
        console.terse("{0}\n".format(self.testMessageJson.__doc__))
        self.bootstrap(kind=raeting.PackKind.json.value)

        mains = []
        mains.append(odict(what="This is a message to the serf. Get to Work", extra="Fix the fence."))

        others = []
        others.append(odict(what="This is a message to the lord. Let me be", extra="Go away."))

        self.message(mains=mains, others=others)

    def testMessageMsgpack(self):
        '''
        Basic messaging with msgpack packing
        '''
        console.terse("{0}\n".format(self.testMessageMsgpack.__doc__))
        self.bootstrap(kind=raeting.PackKind.pack.value)

        mains = []
        mains.append(odict(what="This is a message to the serf. Get to Work", extra="Fix the fence."))

        others = []
        others.append(odict(what="This is a message to the lord. Let me be", extra="Go away."))

        self.message(mains=mains, others=others)

    def testMessageMultipleJson(self):
        '''
        Multiple messages with json packing
        '''
        console.terse("{0}\n".format(self.testMessageMultipleJson.__doc__))
        self.bootstrap(kind=raeting.PackKind.json.value)

        mains = []
        mains.append(odict([('house', "Mama mia1"), ('queue', "fix me")]))
        mains.append(odict([('house', "Mama mia2"), ('queue', "stop me")]))
        mains.append(odict([('house', "Mama mia3"), ('queue', "help me")]))
        mains.append(odict([('house', "Mama mia4"), ('queue', "run me")]))


        others = []
        others.append(odict([('house', "Papa pia1"), ('queue', "fix me")]))
        others.append(odict([('house', "Papa pia1"), ('queue', "stop me")]))
        others.append(odict([('house', "Papa pia1"), ('queue', "help me")]))
        others.append(odict([('house', "Papa pia1"), ('queue', "run me")]))

        self.message(mains=mains, others=others)

    def testMessageMultipleMsgpack(self):
        '''
        multiple messages with msgpack packing
        '''
        console.terse("{0}\n".format(self.testMessageMultipleMsgpack.__doc__))
        self.bootstrap(kind=raeting.PackKind.pack.value)

        mains = []
        mains.append(odict([('house', "Mama mia1"), ('queue', "fix me")]))
        mains.append(odict([('house', "Mama mia2"), ('queue', "stop me")]))
        mains.append(odict([('house', "Mama mia3"), ('queue', "help me")]))
        mains.append(odict([('house', "Mama mia4"), ('queue', "run me")]))


        others = []
        others.append(odict([('house', "Papa pia1"), ('queue', "fix me")]))
        others.append(odict([('house', "Papa pia1"), ('queue', "stop me")]))
        others.append(odict([('house', "Papa pia1"), ('queue', "help me")]))
        others.append(odict([('house', "Papa pia1"), ('queue', "run me")]))
        self.message(mains=mains, others=others)

    def testMessageSectionedJson(self):
        '''
        Sectioned messages with json packing
        '''
        console.terse("{0}\n".format(self.testMessageSectionedJson.__doc__))

        self.bootstrap(kind=raeting.PackKind.json.value)

        #big packets
        stuff = []
        for i in range(10000):
            stuff.append(str(i).rjust(10, " "))
        stuff = "".join(stuff)

        src = ['mayor', self.main.local.name, None]
        dst = ['citizen', self.other.local.name, None]
        route = odict([('src', src), ('dst', dst)])


        mains = []
        mains.append(odict([('route', route), ('content', stuff)]))

        src = ['citizen', self.other.local.name, None]
        dst = ['mayor', self.main.local.name, None]
        route = odict([('src', src), ('dst', dst)])

        others = []
        others.append(odict([('route', route), ('content', stuff)]))

        self.message(mains=mains, others=others, duration=2.0)

    def testMessageSectionedMsgpack(self):
        '''
        Sectioned messages with msgpack packing
        '''
        console.terse("{0}\n".format(self.testMessageSectionedMsgpack.__doc__))

        self.bootstrap(kind=raeting.PackKind.pack.value)

        #big packets
        stuff = []
        for i in range(10000):
            stuff.append(str(i).rjust(10, " "))
        stuff = "".join(stuff)

        src = ['mayor', self.main.local.name, None]
        dst = ['citizen', self.other.local.name, None]
        route = odict([('src', src), ('dst', dst)])


        mains = []
        mains.append(odict([('route', route), ('content', stuff)]))

        src = ['citizen', self.other.local.name, None]
        dst = ['mayor', self.main.local.name, None]
        route = odict([('src', src), ('dst', dst)])

        others = []
        others.append(odict([('route', route), ('content', stuff)]))

        self.message(mains=mains, others=others, duration=2.0)

    def testAutoAccept(self):
        '''
        Basic send auto accept message
        '''
        console.terse("{0}\n".format(self.testAutoAccept.__doc__))

        self.assertTrue(self.main.accept)

        # Don't add remote yard to main so only way to get message from other is
        # if auto acccept works
        self.other.addRemote(yarding.RemoteYard(stack=self.other, ha=self.main.ha))

        self.assertEqual(self.main.name, 'main')
        self.assertEqual(self.main.local.name, 'main')
        self.assertEqual(self.main.ha, os.path.join(self.baseDirpath, 'cherry.main.uxd'))
        self.assertEqual(len(self.main.remotes), 0)

        self.assertEqual(self.other.name, 'other')
        self.assertEqual(self.other.local.name, 'other')
        self.assertEqual(self.other.ha, os.path.join(self.baseDirpath, 'cherry.other.uxd'))
        self.assertEqual(len(self.other.remotes), 1)
        remote = self.other.remotes.values()[0]
        self.assertEqual(remote.ha, os.path.join(self.baseDirpath, 'cherry.main.uxd'))
        self.assertEqual(remote.name, 'main')
        self.assertTrue(remote.uid in self.other.remotes)
        self.assertTrue(remote.name in self.other.nameRemotes)
        self.assertTrue(remote.ha in self.other.haRemotes)
        self.assertIs(self.other.nameRemotes[remote.name], remote)
        self.assertIs(self.other.haRemotes[remote.ha], remote)

        stacking.LaneStack.Pk = raeting.PackKind.pack.value

        others = []
        others.append(odict(what="This is a message to the lord. Let me be", extra="Go away."))

        self.message(mains=[], others=others)

        self.assertEqual(len(self.main.remotes), 1)
        remote = self.main.remotes.values()[0]
        self.assertEqual(remote.ha, os.path.join(self.baseDirpath, 'cherry.other.uxd'))
        self.assertEqual(remote.name, 'other')
        self.assertTrue(remote.uid in self.main.remotes)
        self.assertTrue(remote.name in self.main.nameRemotes)
        self.assertTrue(remote.ha in self.main.haRemotes)
        self.assertIs(self.main.nameRemotes[remote.name], remote)
        self.assertIs(self.main.haRemotes[remote.ha], remote)

        self.main.rxMsgs = deque()
        self.other.rxMsgs = deque()

        mains = []
        mains.append(odict(what="This is a message to the serf. Get to Work", extra="Fix the fence."))

        self.message(mains=mains, others=[])

    def testAutoAcceptNot(self):
        '''
        Basic send non auto accept message
        '''
        console.terse("{0}\n".format(self.testAutoAcceptNot.__doc__))
        self.main.accept =  False
        self.assertIs(self.main.accept, False)

        # Don't add remote yard to main so only way to get message from other is
        # if auto acccept works
        self.other.addRemote(yarding.RemoteYard(stack=self.other, ha=self.main.ha))

        self.assertEqual(self.main.name, 'main')
        self.assertEqual(self.main.local.name, 'main')
        self.assertEqual(self.main.ha, os.path.join(self.baseDirpath, 'cherry.main.uxd'))
        self.assertEqual(len(self.main.remotes), 0)

        self.assertEqual(self.other.name, 'other')
        self.assertEqual(self.other.local.name, 'other')
        self.assertEqual(self.other.ha, os.path.join(self.baseDirpath, 'cherry.other.uxd'))
        self.assertEqual(len(self.other.remotes), 1)
        remote = self.other.remotes.values()[0]
        self.assertEqual(remote.ha, os.path.join(self.baseDirpath, 'cherry.main.uxd'))
        self.assertEqual(remote.name, 'main')

        self.assertTrue(remote.uid in self.other.remotes)
        self.assertTrue(remote.name in self.other.nameRemotes)
        self.assertTrue(remote.ha in self.other.haRemotes)
        self.assertIs(self.other.nameRemotes[remote.name], remote)
        self.assertIs(self.other.haRemotes[remote.ha], remote)

        stacking.LaneStack.Pk = raeting.PackKind.pack.value

        others = []
        others.append(odict(what="This is a message to the lord. Let me be", extra="Go away."))

        for msg in others:
            self.other.transmit(msg=msg)

        self.service()

        self.assertEqual(len(self.main.rxMsgs), 0)
        self.assertEqual(len(self.main.remotes), 0)
        self.assertEqual(self.main.stats['unaccepted_source_yard'], 1)

    def testFetchRemoteFromHa(self):
        '''
        Fetching remote yard by HA
        '''
        console.terse("{0}\n".format(self.testFetchRemoteFromHa.__doc__))
        self.bootstrap(kind=raeting.PackKind.json.value)

        for remote in self.main.remotes.values():
            fetched = self.main.haRemotes.get(remote.ha)
            self.assertIs(remote, fetched)


        for remote in self.other.remotes.values():
            fetched = self.other.haRemotes.get(remote.ha)
            self.assertIs(remote, fetched)

    def testRestart(self):
        '''
        Test messaging after restart
        '''
        console.terse("{0}\n".format(self.testRestart.__doc__))

        stacking.LaneStack.Pk = raeting.PackKind.json.value

        mainData = self.createLaneData(name='main',
                                       uid=1,
                                       base=self.baseDirpath,
                                       lanename='apple')
        main = self.createLaneStack(data=mainData, main=True)
        self.assertTrue(main.ha.endswith(os.path.join('lane','keep','main',
                                                      'apple.main.uxd')))
        self.assertTrue(main.main)

        otherData = self.createLaneData(name='other',
                                        uid=1,
                                        base=self.baseDirpath,
                                        lanename='apple')
        other = self.createLaneStack(data=otherData)
        self.assertTrue(other.ha.endswith(os.path.join('lane','keep','other',
                                                      'apple.other.uxd')))

        main.addRemote(yarding.RemoteYard(stack=main, ha=other.ha))
        self.assertTrue('other' in main.nameRemotes)
        self.assertTrue(other.ha in main.haRemotes)
        other.addRemote(yarding.RemoteYard(stack=other, ha=main.ha))
        self.assertTrue('main' in other.nameRemotes)
        self.assertTrue(main.ha in other.haRemotes)

        src = ['mayor', main.local.name, None] # (house, yard, queue)
        dst = ['citizen', other.local.name, None]
        route = odict([('src', src), ('dst', dst)])
        stuff = "This is my command"
        mains = []
        mains.append(odict([('route', route), ('content', stuff)]))

        src = ['citizen', other.local.name, None]
        dst = ['mayor', main.local.name, None]
        route = odict([('src', src), ('dst', dst)])
        stuff = "This is my reply."
        others = []
        others.append(odict([('route', route), ('content', stuff)]))

        self.messageMainOther(main,  other, mains, others, duration=1.0)

        self.assertEqual(len(main.remotes), 1)
        self.assertTrue('other' in main.nameRemotes)
        self.assertEqual(len(other.remotes), 1)
        self.assertTrue('main' in other.nameRemotes)

        self.assertNotEqual(main.nameRemotes['other'].sid, 0)
        self.assertNotEqual(other.nameRemotes['main'].sid, 0)
        self.assertEqual(main.nameRemotes['other'].rsid,
                         other.nameRemotes['main'].sid)
        self.assertEqual(other.nameRemotes['main'].rsid,
                         main.nameRemotes['other'].sid)

        #now close down  make new stacks
        main.server.close()
        other.server.close()
        main = self.createLaneStack(data=mainData, main=True)
        other = self.createLaneStack(data=otherData)

        main.addRemote(yarding.RemoteYard(stack=main, ha=other.ha))
        self.assertTrue('other' in main.nameRemotes)
        other.addRemote(yarding.RemoteYard(stack=other, ha=main.ha))
        self.assertTrue('main' in other.nameRemotes)

        self.assertEqual(len(main.remotes), 1)
        self.assertEqual(len(other.remotes), 1)

        self.assertNotEqual(main.nameRemotes['other'].sid, 0)
        self.assertNotEqual(other.nameRemotes['main'].sid, 0)
        self.assertEqual(main.nameRemotes['other'].rsid, 0)
        self.assertEqual(other.nameRemotes['main'].rsid, 0)

        self.messageMainOther(main, other, mains, others, duration=1.0)

        self.assertEqual(main.nameRemotes['other'].rsid,
                         other.nameRemotes['main'].sid)
        self.assertEqual(other.nameRemotes['main'].rsid,
                         main.nameRemotes['other'].sid)


        #now close down  make new stacks
        main.server.close()
        other.server.close()
        main = self.createLaneStack(data=mainData, main=True)
        other = self.createLaneStack(data=otherData)

        main.addRemote(yarding.RemoteYard(stack=main, ha=other.ha))
        self.assertTrue('other' in main.nameRemotes)
        other.addRemote(yarding.RemoteYard(stack=other, ha=main.ha))
        self.assertTrue('main' in other.nameRemotes)

        self.assertEqual(len(main.remotes), 1)
        self.assertEqual(len(other.remotes), 1)

        self.assertNotEqual(main.nameRemotes['other'].sid, 0)
        self.assertNotEqual(other.nameRemotes['main'].sid, 0)
        self.assertEqual(main.nameRemotes['other'].rsid, 0)
        self.assertEqual(other.nameRemotes['main'].rsid, 0)

        # now send paginated messages
        src = ['mayor', main.local.name, None] # (house, yard, queue)
        dst = ['citizen', other.local.name, None]
        route = odict([('src', src), ('dst', dst)])
        stuff = ["Do as I say."]
        for i in range(10000):
            stuff.append(str(i).rjust(10, " "))
        stuff = "".join(stuff)
        mains = []
        mains.append(odict([('route', route), ('content', stuff)]))

        src = ['citizen', other.local.name, None]
        dst = ['mayor', main.local.name, None]
        route = odict([('src', src), ('dst', dst)])
        stuff = ["As you wish."]
        for i in range(10000):
            stuff.append(str(i).rjust(10, " "))
        stuff = "".join(stuff)
        others = []
        others.append(odict([('route', route), ('content', stuff)]))

        self.messageMainOther(main, other, mains, others, duration=1.0)

        self.assertEqual(main.nameRemotes['other'].rsid,
                          other.nameRemotes['main'].sid)
        self.assertEqual(other.nameRemotes['main'].rsid,
                          main.nameRemotes['other'].sid)

        #now close down  make new stacks send page at a time
        main.server.close()
        other.server.close()
        main = self.createLaneStack(data=mainData, main=True)
        other = self.createLaneStack(data=otherData)

        main.addRemote(yarding.RemoteYard(stack=main, ha=other.ha))
        self.assertTrue('other' in main.nameRemotes)
        other.addRemote(yarding.RemoteYard(stack=other, ha=main.ha))
        self.assertTrue('main' in other.nameRemotes)

        self.assertEqual(len(main.remotes), 1)
        self.assertEqual(len(other.remotes), 1)

        self.assertNotEqual(main.nameRemotes['other'].sid, 0)
        self.assertNotEqual(other.nameRemotes['main'].sid, 0)
        self.assertEqual(main.nameRemotes['other'].rsid, 0)
        self.assertEqual(other.nameRemotes['main'].rsid, 0)

        for msg in mains:
            main.transmit(msg, uid=main.fetchUidByName(other.local.name))
        for msg in others:
            other.transmit(msg, uid=other.fetchUidByName(main.local.name))


        self.assertEqual(len(main.txMsgs), 1)
        self.assertEqual(len(other.txMsgs), 1)
        self.assertEqual(len(main.nameRemotes['other'].books), 0)
        self.assertEqual(len(other.nameRemotes['main'].books), 0)
        self.assertEqual(len(main.rxMsgs), 0)
        self.assertEqual(len(other.rxMsgs), 0)

        # Now only send and receive one page to/from each side
        self.serviceOneAll(main, other)

        self.assertEqual(len(main.txMsgs), 0)
        self.assertEqual(len(other.txMsgs), 0)
        self.assertEqual(len(main.txes), 1)
        self.assertEqual(len(other.txes), 1)
        self.assertEqual(len(main.nameRemotes['other'].books), 1)
        self.assertEqual(len(other.nameRemotes['main'].books), 1)
        self.assertEqual(len(main.rxMsgs), 0)
        self.assertEqual(len(other.rxMsgs), 0)

        self.assertEqual(main.nameRemotes['other'].rsid,
                          other.nameRemotes['main'].sid)
        self.assertEqual(other.nameRemotes['main'].rsid,
                          main.nameRemotes['other'].sid)

        # save sids
        mainSid = main.nameRemotes['other'].sid
        otherSid = other.nameRemotes['main'].sid

        #now close down one side only, make new stack
        main.server.close()
        main = self.createLaneStack(data=mainData, main=True)
        main.addRemote(yarding.RemoteYard(stack=main, ha=other.ha))

        self.assertEqual(len(main.remotes), 1)
        self.assertEqual(len(other.remotes), 1)

        self.assertNotEqual(main.nameRemotes['other'].sid, mainSid)
        self.assertEqual(other.nameRemotes['main'].sid, otherSid)
        self.assertEqual(main.nameRemotes['other'].rsid, 0)
        self.assertEqual(other.nameRemotes['main'].rsid, mainSid)

        self.assertEqual(len(main.txes), 0)
        self.assertEqual(len(other.txes), 1)
        self.assertEqual(len(main.nameRemotes['other'].books), 0)
        self.assertEqual(len(other.nameRemotes['main'].books), 1)
        self.assertEqual(len(main.rxMsgs), 0)
        self.assertEqual(len(other.rxMsgs), 0)

        # Now remaining page from other (there should be no pages from main)
        self.serviceOneAll(main, other)

        self.assertEqual(main.nameRemotes['other'].rsid,
                          other.nameRemotes['main'].sid)
        self.assertNotEqual(other.nameRemotes['main'].rsid,
                          main.nameRemotes['other'].sid)


        self.assertEqual(len(main.txes), 0)
        self.assertEqual(len(other.txes), 0)
        self.assertEqual(len(main.nameRemotes['other'].books), 0)
        self.assertEqual(len(other.nameRemotes['main'].books), 1)
        self.assertEqual(len(main.rxMsgs), 0)
        self.assertEqual(len(other.rxMsgs), 0)
        self.assertEqual(main.stats['missed_page'], 1)


        #send a new message from main and reap stale book from other
        for msg in mains:
            main.transmit(msg, uid=main.fetchUidByName(other.local.name))

        self.serviceMainOther(main, other, duration=1.0)

        self.assertEqual(main.nameRemotes['other'].rsid,
                          other.nameRemotes['main'].sid)
        self.assertEqual(other.nameRemotes['main'].rsid,
                          main.nameRemotes['other'].sid)
        self.assertEqual(len(main.txes), 0)
        self.assertEqual(len(other.txes), 0)
        self.assertEqual(len(main.nameRemotes['other'].books), 0)
        self.assertEqual(len(other.nameRemotes['main'].books), 0)
        self.assertEqual(len(main.rxMsgs), 0)
        self.assertEqual(len(main.rxMsgs), 0)
        self.assertEqual(len(other.rxMsgs), 1)
        self.assertEqual(other.stats['stale_book'], 1)

        self.assertEqual(len(other.rxMsgs), len(mains))
        for i, duple in enumerate(other.rxMsgs):
            console.terse("Yard '{0}' rxed:\n'{1}'\n".format(other.local.name, duple))
            self.assertDictEqual(mains[i], duple[0])


        # setup to test reset sid numbering by sending single pages to create stale books

        other.rxMsgs.pop()
        for msg in mains:
            main.transmit(msg, uid=main.fetchUidByName(other.local.name))
        for msg in others:
            other.transmit(msg, uid=other.fetchUidByName(main.local.name))

        self.serviceOneAll(main, other)

        self.assertEqual(main.nameRemotes['other'].rsid,
                          other.nameRemotes['main'].sid)
        self.assertEqual(other.nameRemotes['main'].rsid,
                          main.nameRemotes['other'].sid)


        self.assertEqual(len(main.txes), 1)
        self.assertEqual(len(other.txes), 1)
        self.assertEqual(len(main.nameRemotes['other'].books), 1)
        self.assertEqual(len(other.nameRemotes['main'].books), 1)
        self.assertEqual(len(main.rxMsgs), 0)
        self.assertEqual(len(other.rxMsgs), 0)

        # simulate restart that loses msg in queue
        main.txes.pop()
        other.txes.pop()

        src = ['mayor', main.local.name, None] # (house, yard, queue)
        dst = ['citizen', other.local.name, None]
        route = odict([('src', src), ('dst', dst)])
        stuff = "This is my command"
        mains = []
        mains.append(odict([('route', route), ('content', stuff)]))

        src = ['citizen', other.local.name, None]
        dst = ['mayor', main.local.name, None]
        route = odict([('src', src), ('dst', dst)])
        stuff = "This is my reply."
        others = []
        others.append(odict([('route', route), ('content', stuff)]))

        mainSid = main.local.nextSid()
        otherSid = other.local.nextSid()
        main.nameRemotes['other'].sid = mainSid
        other.nameRemotes['main'].sid = otherSid
        for msg in mains:
            main.transmit(msg, uid=main.fetchUidByName(other.local.name))
        for msg in others:
            other.transmit(msg,  uid=other.fetchUidByName(main.local.name))

        self.serviceOneAll(main, other)

        self.assertEqual(main.nameRemotes['other'].sid, mainSid)
        self.assertEqual(other.nameRemotes['main'].sid, otherSid)
        self.assertEqual(main.nameRemotes['other'].rsid, otherSid)
        self.assertEqual(other.nameRemotes['main'].rsid, mainSid)

        self.assertEqual(len(main.txes), 0)
        self.assertEqual(len(other.txes), 0)
        self.assertEqual(len(main.nameRemotes['other'].books), 0)
        self.assertEqual(len(other.nameRemotes['main'].books), 0)
        self.assertEqual(len(main.rxMsgs), 1)
        self.assertEqual(len(other.rxMsgs), 1)
        self.assertEqual(main.stats['stale_book'], 1)
        self.assertEqual(other.stats['stale_book'], 2)

        main.server.close()
        other.server.close()
Esempio n. 17
0
class BasicLoadTestCase(unittest.TestCase):
    '''
    Base class for load tests. Provides generic master/slave runners with flooding and checking logic.
    '''

    def __init__(self, *args, **kwargs):
        super(BasicLoadTestCase, self).__init__(*args, **kwargs)

    def setUp(self):
        self.store = Store(stamp=0.0)
        self.timer = StoreTimer(store=self.store, duration=1.0)
        self.baseDirpath = tempfile.mkdtemp(prefix="raet",  suffix="base", dir=TEMPDIR)
        self.stack = None
        # This has to be set to True in the only one process that would perform tearDown steps
        self.engine = True
        stacking.RoadStack.BurstSize = 100

    def tearDown(self):
        # The only engine have to tear down stacks after all workers have done
        if not self.engine:
            if self.stack:
                self.stack.server.close()
                self.stack.clearAllDir()
        else:
            if os.path.exists(self.baseDirpath):
                shutil.rmtree(self.baseDirpath)

    def createStack(self, name, port):
        '''
        Create a RoadStack object bound to the specified port on localhost.

        :param name: stack name
        :param port: port to bind to
        :return: the stack
        '''
        dirpath = os.path.join(self.baseDirpath, 'road', 'keep', name)
        signer = nacling.Signer()
        mainSignKeyHex = signer.keyhex
        privateer = nacling.Privateer()
        mainPriKeyHex = privateer.keyhex

        keeping.clearAllKeep(dirpath)

        return stacking.RoadStack(store=self.store,
                                  name=name,
                                  main=True,
                                  auto=raeting.AutoMode.once.value,
                                  ha=("", port),
                                  sigkey=mainSignKeyHex,
                                  prikey=mainPriKeyHex,
                                  dirpath=dirpath,
                                  )

    def joinAll(self, initiator, correspondentAddresses, timeout=None, retryCount=1):
        '''
        Utility method to do join.

        :param initiator: join initiator stack
        :param correspondentAddresses: an iterable object containing ha tuples
        :param timeout: join timeout, will be passed to stack
        :param retryCount: retry join max the specified count of times until success
        '''
        console.terse("\nJoin Transaction **************\n")
        addresses = list(correspondentAddresses)
        for ha in addresses:
            initiator.addRemote(estating.RemoteEstate(stack=initiator,
                                                      fuid=0,  # vacuous join
                                                      sid=0,  # always 0 for join
                                                      ha=ha))
        while retryCount > 0:
            done = True
            for remote in initiator.remotes.values():
                if not remote.joined:
                    done = False
                    initiator.join(uid=remote.uid, timeout=timeout)
            self.serviceOne(initiator)
            if done:
                break
            retryCount -= 1

    def allowAll(self, initiator, timeout=None, retryCount=1):
        '''
        Utility method to do allow.

        :param initiator: allow initiator stack
        :param timeout: allow timeout, will be passed to stack
        :param retryCount: retry allow max the specified count of times until success
        '''
        console.terse("\nAllow Transaction **************\n")
        while retryCount > 0:
            done = True
            for remote in initiator.remotes.values():
                if not remote.allowed:
                    done = False
                    initiator.allow(uid=remote.uid, timeout=timeout)
            self.serviceOne(initiator)
            if done:
                break
            retryCount -= 1

    def serviceAll(self, stacks, duration=100.0, timeout=0.0, step=0.1, exitCase=None):
        '''
        Utility method to service queues.

        :param stacks: iterable containing stacks to service
        :param duration: max service duration, actually could finish earlier if there still no transactions to service
        :param timeout: time to continue service even if there are no transactions
        :param step: timer step
        :param exitCase: stop immediately if this function return true
        :return: actual exit reason, could be one of 'duration', 'exitCase' or 'timeout'
        '''
        self.timer.restart(duration=duration)
        empty = False
        elapsed = self.timer.getElapsed()
        retReason = 'duration'
        while not self.timer.expired:
            for stack in stacks:
                stack.serviceAll()
            if any(stack.transactions for stack in stacks):
                empty = False  # reset nop timeout
            else:
                if exitCase and exitCase():
                    retReason = 'exitCase'
                    break
                if empty:
                    if self.timer.getElapsed() - elapsed > timeout:
                        retReason = 'timeout'
                        break
                else:
                    empty = True
                    elapsed = self.timer.getElapsed()
            self.store.advanceStamp(step)
            time.sleep(step)
        return retReason

    def serviceOne(self, stack, duration=100.0, timeout=0.0, step=0.1, exitCase=None):
        '''
        Utility method to service one stack. See :func:`serviceStacks`.
        '''
        return self.serviceAll([stack], duration, timeout, step, exitCase)

    def bidirectional(self, stack, duration=3.0):
        '''
        Simultaneously sends and receives test messages.
            - self.msgSize: message size
            - self.msgCount: per remote message count

        :param stack: the stack to service
        :param duration: service duration to prevent hang
        '''
        console.terse("\nMessages Bidirectional {0} *********\n".format(stack.name))
        verifier = data.MessageVerifier(size=self.msgSize, msgCount=self.msgCount, remoteCount=len(stack.remotes),
                                        house='manor', queue='stuff')
        received = 0
        expected = len(stack.remotes) * self.msgCount
        maxTransactions = MAX_TRANSACTIONS * len(stack.remotes)
        for msg in data.generateMessages(name=stack.name, size=self.msgSize, count=self.msgCount):
            for remote in stack.remotes.values():
                # send message
                stack.transmit(msg, uid=remote.uid)
                # service while there are too much transactions (but loops at least once)
                while True:
                    self.serviceOne(stack, duration=0.01, step=0.01)
                    # check received
                    if stack.rxMsgs:
                        received += len(stack.rxMsgs)
                        while stack.rxMsgs:
                            verifier.verifyMessage(stack.rxMsgs.popleft())
                    # keep servicing if there are a lot of transactions
                    if len(stack.transactions) <= maxTransactions:
                        break

        # all sent, continue handle received
        while received < expected:
            self.serviceOne(stack, duration=duration, timeout=duration,
                            exitCase=lambda: stack.rxMsgs)
            # if received nothing during timeout, assume we're done
            if not stack.rxMsgs:
                break
            received += len(stack.rxMsgs)
            while stack.rxMsgs:
                verifier.verifyMessage(stack.rxMsgs.popleft())

        # wait remaining messenger transactions if any to be closed
        if stack.transactions:
            self.serviceOne(stack, duration=duration)

        # Done. Wait others
        self.doneCounter.inc()
        elapsed = 0.0  # be safe from engine unexpected stop
        step = 1.0
        while not self.stopFlag.get() and elapsed < duration * 2:
            self.serviceOne(stack, duration=step, timeout=step)
            elapsed += step

        # check result
        console.terse("\nStack '{0}' uid={1}\n\tTransactions: {2}\n\trcv/exp: {3}/{4}\n\tStats: {5}\n"
                      .format(stack.name, stack.local.uid, stack.transactions, received, expected, stack.stats))
        rcvErrors = verifier.checkAllDone()
        if rcvErrors:
            console.terse("{0} received message with the following errors:\n".format(stack.name))
            for s in rcvErrors:
                console.terse("\t{0} from {1}\n".format(stack.name, s))
        self.assertEqual(len(stack.transactions), 0)
        self.assertEqual(len(rcvErrors), 0)
        self.assertEqual(received, expected)

    def send(self, stack, duration=3.0):
        '''
        Sends test messages.
            - self.msgSize: message size
            - self.msgCount: per remote message count

        :param stack: the stack to service
        :param duration: service duration to prevent hang
        '''
        console.terse("\nMessages Sender {0} *********\n".format(stack.name))
        maxTransactions = MAX_TRANSACTIONS * len(stack.remotes)
        for msg in data.generateMessages(name=stack.name, size=self.msgSize, count=self.msgCount):
            for remote in stack.remotes.values():
                # send message
                stack.transmit(msg, uid=remote.uid)
                # service while there are too much transactions (but loops at least once)
                while True:
                    self.serviceOne(stack, duration=0.01, step=0.01)
                    if len(stack.transactions) <= maxTransactions:
                        break

        # done, wait others
        self.doneCounter.inc()
        elapsed = 0.0  # be safe from engine unexpected stop
        step = 1.0
        while not self.stopFlag.get() and elapsed < duration * 2:
            self.serviceOne(stack, duration=step, timeout=step)
            elapsed += step

        # check result
        console.terse("\nStack '{0}' uid={1}\n\tTransactions: {2}\n\tStats: {3}\n"
                      .format(stack.name, stack.local.uid, stack.transactions, stack.stats))
        self.assertEqual(len(stack.transactions), 0)
        self.assertEqual(len(stack.rxMsgs), 0)

    def receive(self, stack, duration=3.0):
        '''
        Receives test messages.
            - self.msgSize: message size
            - self.msgCount: per remote message count

        :param stack: the stack to service
        :param duration: service duration to prevent hang
        '''
        console.terse("\nMessages Receiver {0} *********\n".format(stack.name))
        verifier = data.MessageVerifier(size=self.msgSize, msgCount=self.msgCount, remoteCount=len(stack.remotes),
                                        house='manor', queue='stuff')

        # Receive messages
        received = 0
        expected = len(stack.remotes) * self.msgCount
        while received < expected:
            reason = self.serviceOne(stack, duration=duration, timeout=duration,
                                     exitCase=lambda: stack.rxMsgs)
            console.terse("{0} service exit reason: {1}, transactions: {2}\n".format(
                stack.name, reason, stack.transactions))
            # received nothing during timeout, assume there is nothing to receive
            if not stack.rxMsgs:
                break
            received += len(stack.rxMsgs)
            while stack.rxMsgs:
                verifier.verifyMessage(stack.rxMsgs.popleft())

        # Done. Wait others
        self.doneCounter.inc()
        elapsed = 0.0  # be safe from engine unexpected stop
        step = 1.0
        while not self.stopFlag.get() and elapsed < duration * 2:
            self.serviceOne(stack, duration=step, timeout=step)
            elapsed += step

        # Verify results
        console.terse("\nStack '{0}' uid={1}\n\tTransactions: {2}\n\trcv/exp: {3}/{4}\n\tStats: {5}\n"
                      .format(stack.name, stack.local.uid, stack.transactions, received, expected, stack.stats))
        for t in stack.transactions:
            if isinstance(t, transacting.Messengent):
                print("Tray lost segments: {0}\n".format([i for i, x in enumerate(t.tray.segments) if x is None]))
        rcvErrors = verifier.checkAllDone()
        if rcvErrors:
            console.terse("{0} received message with the following errors:\n".format(stack.name))
            for s in rcvErrors:
                console.terse("\t{0} from {1}\n".format(stack.name, s))
        self.assertEqual(len(stack.transactions), 0)
        self.assertEqual(len(rcvErrors), 0)
        self.assertEqual(received, expected)

    def masterPeer(self, name, port, minionCount, action):
        '''
        Master peer main function. It creates a RoadStack, sends/receives a bunch of test messages to remote peers and
        checks the result

        :param name: the name of this peer
        :param port: port to bind to
        :param minionCount: count of remote peers
        :param action: action function. Could be one of self.send, self.receive or self.bidirectional
        :return: if done without errors the process will end with 0 exit code
        '''
        self.engine = False
        # create stack
        self.stack = self.createStack(name, port)

        console.terse("\nCreated {0} at {1} *********\n".format(name, self.stack.ha))

        console.terse("\n{0} Waiting For Minions *********\n".format(name))

        # wait minions to join and allow
        def isBootstrapDone():
            if not self.stack.remotes:
                return False
            if len(self.stack.remotes) != minionCount:
                return False
            for rmt in self.stack.remotes.values():
                if not rmt.allowed:
                    return False
            return True

        serviceCode = self.serviceOne(self.stack, timeout=100.0, exitCase=isBootstrapDone)

        console.terse("\n{0} bootstrap done with code {1}".format(name, serviceCode))

        # verify all minions connected
        self.assertEqual(len(self.stack.transactions), 0)
        self.assertEqual(len(self.stack.remotes), minionCount)
        for remote in self.stack.remotes.values():
            self.assertTrue(remote.joined)
            self.assertTrue(remote.allowed)

        console.terse("\n{0} Bootstrap Done ({1}) *********\n".format(name, serviceCode))
        # do the job
        action(self.stack, duration=self.duration)

    def minionPeer(self, name, port, remoteAddresses, action):
        '''
        Slave peer main function. It creates a RoadStack, sends/receives a bunch of test messages to remote peers and
        checks the result

        :param name: the name of this peer
        :param port: port to bind to
        :param minionCount: count of remote peers
        :param action: action function. Could be one of self.send, self.receive or self.bidirectional
        :return: if done without errors the process will end with 0 exit code
        '''
        self.engine = False
        # Create stack
        self.stack = self.createStack(name, port)

        console.terse("\nCreated {0} at {1} *********\n".format(name, self.stack.ha))

        console.terse("\n{0} Joining Remotes *********\n".format(name))
        # join with all masters
        self.joinAll(self.stack, remoteAddresses, retryCount=10)
        self.assertEqual(len(self.stack.transactions), 0)
        self.assertEqual(len(self.stack.remotes), len(remoteAddresses))
        for remote in self.stack.remotes.values():
            self.assertTrue(remote.joined, "{0}: remote '{1}' at {2} is not joined".format(
                name, remote.name, remote.ha))

        console.terse("\n{0} Allowing Remotes *********\n".format(name))
        # allow all masters
        self.allowAll(self.stack, retryCount=10)
        self.assertEqual(len(self.stack.transactions), 0)
        self.assertEqual(len(self.stack.remotes), len(remoteAddresses))
        for remote in self.stack.remotes.values():
            self.assertTrue(remote.allowed, "{0}: remote '{1}' at {2} is not allowed".format(
                name, remote.name, remote.ha))

        console.terse("\n{0} Bootstrap Done *********\n".format(name))
        # do the job
        action(self.stack, duration=self.duration)

    def messagingMultiPeers(self,
                            masterCount,
                            minionCount,
                            msgSize,
                            msgCount,
                            duration,
                            direction):
        '''
        Perform the test. This function creates a number of processes which are master and slave network peers. Each
        process sends a bunch of messages to each process from the opposite group (for bidirectional case)

        :param masterCount: count of master peers
        :param minionCount: count of slave peers
        :param msgSize: size of the message stuff, actually the message would be bigger
        :param msgCount: count of messages to be sent from peer to peer
        :param duration: test duration limit, will be used in service calls so actual allowed duration will be greater
        :param direction: sending duration, could be one of DIR_TO_MASTER, DIR_FROM_MASTER or DIR_BIDIRECTIONAL
        :return: throw PyUnit assertion if fail
        '''
        self.masterCount = masterCount
        self.minionCount = minionCount
        self.msgSize = msgSize
        self.msgCount = msgCount
        self.duration = duration
        masterDir = {DIR_BIDIRECTIONAL: self.bidirectional,
                     DIR_TO_MASTER:     self.receive,
                     DIR_FROM_MASTER:   self.send}
        minionDir = {DIR_BIDIRECTIONAL: self.bidirectional,
                     DIR_TO_MASTER:     self.send,
                     DIR_FROM_MASTER:   self.receive}

        # create masters
        port = raeting.RAET_PORT
        masterHostAddresses = []
        masterProcs = []
        self.doneCounter = mp_helper.Counter()
        self.stopFlag = mp_helper.Counter()
        for i in xrange(masterCount):
            masterHostAddresses.append(('', port))
            name = 'master{0}'.format(i)
            masterProc = multiprocessing.Process(target=self.masterPeer,
                                                 name=name,
                                                 args=(name,
                                                       port,
                                                       minionCount,
                                                       masterDir[direction]))
            masterProcs.append(masterProc)
            port += 1

        # create minions
        minionProcs = []
        for i in xrange(minionCount):
            name = 'minion{0}'.format(i)
            minionProc = multiprocessing.Process(target=self.minionPeer,
                                                 name=name,
                                                 args=(name,
                                                       port,
                                                       masterHostAddresses,
                                                       minionDir[direction]))
            minionProcs.append(minionProc)
            port += 1

        # start masters first
        for proc in masterProcs:
            proc.start()
        time.sleep(1.0)  # let masters start
        # start minions
        for proc in minionProcs:
            proc.start()

        # wait until all workers done their job
        elapsed = 0.0  # be safe from worker unexpected stop
        step = 1.0
        while self.doneCounter.get() < masterCount + minionCount and elapsed < duration * 2:
            time.sleep(step)
            elapsed += step
        # set stop flag
        self.stopFlag.inc()

        # wait all processes exited
        for procs in (masterProcs, minionProcs):
            for proc in procs:
                proc.join()

        # ensure all workers successfully done
        for procs in (masterProcs, minionProcs):
            for proc in procs:
                self.assertEqual(proc.exitcode, 0, "'{0}' returned {1}".format(proc.name, proc.exitcode))
Esempio n. 18
0
 def setUp(self):
     self.store = Store(stamp=0.0)
     self.timer = StoreTimer(store=self.store, duration=1.0)
Esempio n. 19
0
    def __init__(
        self,
        store=None,
        version=raeting.VERSION,
        main=None,
        puid=None,
        local=None,  #passed up from subclass
        name='',
        uid=None,
        server=None,
        ha=None,
        bufcnt=2,
        rxMsgs=None,
        txMsgs=None,
        rxes=None,
        txes=None,
        stats=None,
    ):
        '''
        Setup Stack instance
        '''
        self.store = store or Store(stamp=0.0)

        self.version = version
        self.main = main

        if getattr(self, 'puid', None) is None:
            self.puid = puid if puid is not None else self.Uid

        self.local = local or lotting.Lot(
            stack=self,
            name=name,
            uid=uid,
            ha=ha,
        )
        self.local.stack = self

        self.remotes = self.uidRemotes = odict()  # remotes indexed by uid
        self.nameRemotes = odict()  # remotes indexed by name

        self.bufcnt = bufcnt
        if not server:
            server = self.serverFromLocal()

        self.server = server
        if self.server:
            if not self.server.reopen():  # open socket
                raise raeting.StackError(
                    "Stack '{0}': Failed opening server at"
                    " '{1}'\n".format(self.name, self.server.ha))

            self.ha = self.server.ha  # update local host address after open

            console.verbose("Stack '{0}': Opened server at '{1}'\n".format(
                self.name, self.ha))

        self.rxMsgs = rxMsgs if rxMsgs is not None else deque(
        )  # messages received
        self.txMsgs = txMsgs if txMsgs is not None else deque(
        )  # messages to transmit
        self.rxes = rxes if rxes is not None else deque(
        )  # udp packets received
        self.txes = txes if txes is not None else deque(
        )  # udp packet to transmit
        self.stats = stats if stats is not None else odict()  # udp statistics
        self.statTimer = StoreTimer(self.store)
Esempio n. 20
0
 def addDoneTransaction(self, index):
     self.doneTransactions[index] = StoreTimer(
         self.stack.store, duration=self.stack.MsgStaleTimeout)
Esempio n. 21
0
class RemoteEstate(Estate):
    '''
    RAET protocol endpoint remote estate object ie Remote Road Lot
    Maintains verifier for verifying signatures and publican for encrypt/decrypt

    .alived attribute is the dead or alive status of the remote

    .alived = True, alive, recently have received valid signed packets from remote
    .alive = False, dead, recently have not received valid signed packets from remote

    .fuid is the far uid of the remote as owned by the farside stack
    '''

    def __init__(self,
                 stack,
                 uid=None,
                 fuid=0,
                 main=False,
                 kind=0,
                 verkey=None,
                 pubkey=None,
                 acceptance=None,
                 joined=None,
                 rsid=0,
                 **kwa):
        '''
        Setup instance

        stack is required parameter

        verkey is either nacl VerifyKey or raw or hex encoded key
        pubkey is either nacl PublicKey or raw or hex encoded key

        acceptance is accepted state of remote on Road

        rsid is last received session id used by remotely initiated transaction


        '''
        if uid is None:
            uid = stack.nextUid()
            while uid in stack.remotes or uid == stack.local.uid:
                uid = stack.nextUid()

        if 'ha' not in kwa:
            kwa['ha'] = ('127.0.0.1', raeting.RAET_TEST_PORT)
        super(RemoteEstate, self).__init__(stack, uid=uid, **kwa)
        self.fuid = fuid
        self.main = main
        self.kind = kind
        self.joined = joined
        self.allowed = None
        self.alived = None
        self.reaped = None
        self.acceptance = acceptance
        self.privee = nacling.Privateer() # short term key manager
        self.publee = nacling.Publican() # correspondent short term key  manager
        self.verfer = nacling.Verifier(verkey) # correspondent verify key manager
        self.pubber = nacling.Publican(pubkey) # correspondent long term key manager

        self.rsid = rsid # last sid received from remote when RmtFlag is True

        # persistence keep alive heartbeat timer. Initial duration has offset so
        # not synced with other side persistence heatbeet
        # by default do not use offset on main
        if self.stack.main:
            duration = self.stack.period
        else:
            duration = self.stack.period + self.stack.offset
        self.timer = StoreTimer(store=self.stack.store,
                                       duration=duration)

        self.reapTimer = StoreTimer(self.stack.store,
                                           duration=self.stack.interim)
        self.messages = deque() # deque of saved stale message body data to remote.uid

    @property
    def nuid(self):
        '''
        property that returns nuid, near uid, of remote as owned by nearside stack
        alias for uid
        '''
        return self.uid

    @nuid.setter
    def nuid(self, value):
        '''
        setter for nuid, near uid, property
        '''
        self.uid = value

    @property
    def juid(self):
        '''
        property that returns juid, join uid, duple of (nuid, fuid)
        nuid is near uid
        fuid is far uid as owned by farside stack
        '''
        return (self.nuid, self.fuid)

    @juid.setter
    def juid(self, value):
        '''
        setter for juid, join uid, property, value is duple of (nuid, fuid)
        '''
        self.nuid, self.fuid = value

    def rekey(self):
        '''
        Regenerate short term keys
        '''
        self.allowed = None
        self.privee = nacling.Privateer() # short term key
        self.publee = nacling.Publican() # correspondent short term key  manager

    def validRsid(self, rsid):
        '''
        Compare new rsid to old .rsid and return True
        If old is zero Then new is always valid
        If new is >= old modulo N where N is 2^32 = 0x100000000
        And >= means the difference is less than N//2 = 0x80000000
        (((new - old) % 0x100000000) < (0x100000000 // 2))
        '''
        return self.validateSid(new=rsid, old=self.rsid)

    def refresh(self, alived=True):
        '''
        Restart presence heartbeat timer and conditionally reapTimer
        If alived is None then do not change .alived  but update timer
        If alived is True then set .alived to True and handle implications
        If alived is False the set .alived to False and handle implications
        '''
        self.timer.restart(duration=self.stack.period)
        if alived is None:
            return

        if self.alived or alived: # alive before or after
            self.reapTimer.restart()
            if self.reaped:
                self.unreap()
        #otherwise let timer run both before and after are still dead
        self.alived = alived

    def manage(self, cascade=False, immediate=False):
        '''
        Perform time based processing of keep alive heatbeat
        '''
        if not self.reaped: # only manage alives if not already reaped
            if immediate or self.timer.expired:
                # alive transaction restarts self.timer
                self.stack.alive(uid=self.uid, cascade=cascade)
            if self.stack.interim >  0.0 and self.reapTimer.expired:
                self.reap()

    def reap(self):
        '''
        Remote is dead, reap it if main estate.
        '''
        if self.stack.main: # only main can reap
            console.concise("Stack {0}: Reaping dead remote {1} at {2}\n".format(
                    self.stack.name, self.name, self.stack.store.stamp))
            self.stack.incStat("remote_reap")
            self.reaped = True

    def unreap(self):
        '''
        Remote packet received from remote so not dead anymore.
        '''
        if self.stack.main: # only only main can reap or unreap
            console.concise("Stack {0}: Unreaping dead remote {1} at {2}\n".format(
                    self.stack.name, self.name, self.stack.store.stamp))
            self.stack.incStat("remote_unreap")
            self.reaped = False

    def removeStaleCorrespondents(self):
        '''
        Remove local stale correspondent transactions associated with remote

        Local correspondent is indicated by rf ==True

        Stale means the sid in the transaction is older than the current .rsid
        assuming neither is zero, that is sid in index is older than remote.rsid

        old rsid == 0 means new always valid

        When sid in index is older than remote.rsid
        Where index is tuple: (rf, le, re, si, ti, bf,)
            rf = Remotely Initiated Flag, RmtFlag
            le = leid, Local estate ID, LEID
            re = reid, Remote estate ID, REID
            si = sid, Session ID, SID
            ti = tid, Transaction ID, TID
            bf = Broadcast Flag, BcstFlag
        '''
        for index, transaction in self.transactions.items():
            sid = index[3]
            rf = index[0] # correspondent
            if rf and not self.validRsid(sid):
                transaction.nack()
                self.removeTransaction(index)
                emsg = ("Stack {0}: Stale correspondent {1} from remote {1} at {2}"
                            "\n".format(self.stack.name,
                                        index,
                                        self.name,
                                        self.stack.store.stamp))
                console.terse(emsg)
                self.stack.incStat('stale_correspondent')
        self.doneTransactions.clear()

    def replaceStaleInitiators(self):
        '''
        Save and remove any messages from messenger transactions initiated locally
        with remote

        Remove non message stale local initiator transactions associated with remote
        Also save and requeue any stale locally initiated message transactions.

        Local inititors have rf flag == False

        Stale means the sid in the transaction is older than the current .sid
        assuming neither is zero, that is  sid in index is older than remote.sid

        old sid == 0 means new always valid

        Where index is tuple: (rf, le, re, si, ti, bf,)
            rf = Remotely Initiated Flag, RmtFlag
            le = leid, Local estate ID, LEID
            re = reid, Remote estate ID, REID
            si = sid, Session ID, SID
            ti = tid, Transaction ID, TID
            bf = Broadcast Flag, BcstFlag
        '''
        for index, transaction in self.transactions.items():
            rf = index[0]
            sid = index[3]

            if not rf and not self.validSid(sid): # transaction sid newer or equal
                if transaction.kind in [TrnsKind.message]:
                    self.saveMessage(transaction)
                transaction.nack()
                self.removeTransaction(index)
                emsg = ("Stack {0}: Stale initiator {1} to remote {2} at {3}"
                        "\n".format(self.stack.name,
                                    index,
                                    self.name,
                                    self.stack.store.stamp))
                console.terse(emsg)
                self.stack.incStat('stale_initiator')

    def saveMessage(self, messenger):
        '''
        Save copy of body data from stale initiated messenger onto .messages deque
        for retransmitting later after new session is established
        messenger is instance of Messenger compatible transaction
        '''
        self.messages.append(odict(messenger.tray.body))
        emsg = ("Stack {0}: Saved stale message with remote {1}"
                                                "\n".format(self.stack.name,
                                                            self.name))
        console.concise(emsg)

    def sendSavedMessages(self):
        '''
        Message is Messenger compatible transaction
        Save stale initiated message for retransmitting later after new session is established
        '''
        while self.messages:
            body = self.messages.popleft()
            self.stack.message(body, uid=self.uid)
            emsg = ("Stack {0}: Resent saved message with remote {1}"
                                        "\n".format(self.stack.name, self.name))
            console.concise(emsg)

    def allowInProcess(self):
        '''
        Returns list of transactions for all allow transactions with this remote
        that are already in process
        '''
        return ([t for t in self.transactions.values()
                     if t.kind == TrnsKind.allow])

    def joinInProcess(self):
        '''
        Returns  list of transactions for all join transaction with this remote
        that are already in process
        '''
        return ([t for t in self.transactions.values()
                     if t.kind == TrnsKind.join])
Esempio n. 22
0
    def __init__(self,
                 stack,
                 uid=None,
                 fuid=0,
                 main=False,
                 kind=0,
                 verkey=None,
                 pubkey=None,
                 acceptance=None,
                 joined=None,
                 rsid=0,
                 **kwa):
        '''
        Setup instance

        stack is required parameter

        verkey is either nacl VerifyKey or raw or hex encoded key
        pubkey is either nacl PublicKey or raw or hex encoded key

        acceptance is accepted state of remote on Road

        rsid is last received session id used by remotely initiated transaction


        '''
        if uid is None:
            uid = stack.nextUid()
            while uid in stack.remotes or uid == stack.local.uid:
                uid = stack.nextUid()

        if 'ha' not in kwa:
            kwa['ha'] = ('127.0.0.1', raeting.RAET_TEST_PORT)
        super(RemoteEstate, self).__init__(stack, uid=uid, **kwa)
        self.fuid = fuid
        self.main = main
        self.kind = kind
        self.joined = joined
        self.allowed = None
        self.alived = None
        self.reaped = None
        self.acceptance = acceptance
        self.privee = nacling.Privateer()  # short term key manager
        self.publee = nacling.Publican(
        )  # correspondent short term key  manager
        self.verfer = nacling.Verifier(
            verkey)  # correspondent verify key manager
        self.pubber = nacling.Publican(
            pubkey)  # correspondent long term key manager

        self.rsid = rsid  # last sid received from remote when RmtFlag is True

        # persistence keep alive heartbeat timer. Initial duration has offset so
        # not synced with other side persistence heatbeet
        # by default do not use offset on main
        if self.stack.main:
            duration = self.stack.period
        else:
            duration = self.stack.period + self.stack.offset
        self.timer = StoreTimer(store=self.stack.store, duration=duration)

        self.reapTimer = StoreTimer(self.stack.store,
                                    duration=self.stack.interim)
        self.messages = deque(
        )  # deque of saved stale message body data to remote.uid
Esempio n. 23
0
    def __init__(self,
                 stack,
                 uid=None,
                 fuid=0,
                 main=False,
                 kind=0,
                 verkey=None,
                 pubkey=None,
                 acceptance=None,
                 joined=None,
                 rsid=0,
                 **kwa):
        '''
        Setup instance

        stack is required parameter

        verkey is either nacl VerifyKey or raw or hex encoded key
        pubkey is either nacl PublicKey or raw or hex encoded key

        acceptance is accepted state of remote on Road

        rsid is last received session id used by remotely initiated transaction


        '''
        if uid is None:
            uid = stack.nextUid()
            while uid in stack.remotes or uid == stack.local.uid:
                uid = stack.nextUid()

        if 'ha' not in kwa:
            kwa['ha'] = ('127.0.0.1', raeting.RAET_TEST_PORT)
        super(RemoteEstate, self).__init__(stack, uid=uid, **kwa)
        self.fuid = fuid
        self.main = main
        self.kind = kind
        self.joined = joined
        self.allowed = None
        self.alived = None
        self.reaped = None
        self.acceptance = acceptance
        self.privee = nacling.Privateer() # short term key manager
        self.publee = nacling.Publican() # correspondent short term key  manager
        self.verfer = nacling.Verifier(verkey) # correspondent verify key manager
        self.pubber = nacling.Publican(pubkey) # correspondent long term key manager

        self.rsid = rsid # last sid received from remote when RmtFlag is True

        # persistence keep alive heartbeat timer. Initial duration has offset so
        # not synced with other side persistence heatbeet
        # by default do not use offset on main
        if self.stack.main:
            duration = self.stack.period
        else:
            duration = self.stack.period + self.stack.offset
        self.timer = StoreTimer(store=self.stack.store,
                                       duration=duration)

        self.reapTimer = StoreTimer(self.stack.store,
                                           duration=self.stack.interim)
        self.messages = deque() # deque of saved stale message body data to remote.uid
Esempio n. 24
0
    def testValetServiceBottleStream(self):
        """
        Test Valet WSGI service request response stream sse
        """
        console.terse("{0}\n".format(
            self.testValetServiceBottleStream.__doc__))

        try:
            import bottle
        except ImportError as ex:
            console.terse("Bottle not available.\n")
            return

        store = storing.Store(stamp=0.0)

        app = bottle.default_app()  # create bottle app

        @app.get('/stream')
        def streamGet():
            """
            Create test server sent event stream that sends count events
            """
            timer = StoreTimer(store, duration=2.0)
            bottle.response.set_header('Content-Type',
                                       'text/event-stream')  #text
            bottle.response.set_header('Cache-Control', 'no-cache')
            # HTTP 1.1 servers detect text/event-stream and use Transfer-Encoding: chunked
            # Set client-side auto-reconnect timeout, ms.
            yield 'retry: 1000\n\n'
            i = 0
            yield 'id: {0}\n'.format(i)
            i += 1
            yield 'data: START\n\n'
            n = 1
            while not timer.expired:
                yield 'id: {0}\n'.format(i)
                i += 1
                yield 'data: {0}\n\n'.format(n)
                n += 1
            yield "data: END\n\n"

        console.terse("{0}\n".format("Building Valet ...\n"))
        wireLogAlpha = wiring.WireLog(buffify=True, same=True)
        result = wireLogAlpha.reopen()

        alpha = serving.Valet(port=6101,
                              bufsize=131072,
                              wlog=wireLogAlpha,
                              store=store,
                              app=app)
        self.assertIs(alpha.servant.reopen(), True)
        self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101))
        self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101))

        console.terse("{0}\n".format("Building Patron ...\n"))
        wireLogBeta = wiring.WireLog(buffify=True, same=True)
        result = wireLogBeta.reopen()

        path = "http://{0}:{1}/".format('localhost', alpha.servant.eha[1])

        beta = clienting.Patron(
            bufsize=131072,
            wlog=wireLogBeta,
            store=store,
            path=path,
            reconnectable=True,
        )

        self.assertIs(beta.connector.reopen(), True)
        self.assertIs(beta.connector.accepted, False)
        self.assertIs(beta.connector.connected, False)
        self.assertIs(beta.connector.cutoff, False)

        request = odict([
            ('method', u'GET'),
            ('path', u'/stream'),
            ('qargs', odict()),
            ('fragment', u''),
            ('headers',
             odict([('Accept', 'application/json'), ('Content-Length', 0)])),
            ('body', None),
        ])

        beta.requests.append(request)
        timer = StoreTimer(store, duration=1.0)
        while (not timer.expired):
            alpha.serviceAll()
            time.sleep(0.05)
            beta.serviceAll()
            time.sleep(0.05)
            store.advanceStamp(0.1)

        self.assertIs(beta.connector.accepted, True)
        self.assertIs(beta.connector.connected, True)
        self.assertIs(beta.connector.cutoff, False)

        self.assertEqual(len(alpha.servant.ixes), 1)
        self.assertEqual(len(alpha.reqs), 1)
        self.assertEqual(len(alpha.reps), 1)
        requestant = alpha.reqs.values()[0]
        self.assertEqual(requestant.method, request['method'])
        self.assertEqual(requestant.url, request['path'])
        self.assertEqual(
            requestant.headers, {
                'accept': 'application/json',
                'accept-encoding': 'identity',
                'content-length': '0',
                'host': 'localhost:6101'
            })

        #timed out while stream still open so no responses in .responses
        self.assertIs(beta.waited, True)
        self.assertIs(beta.respondent.ended, False)
        self.assertEqual(len(beta.responses), 0)
        self.assertIn('content-type', beta.respondent.headers)
        self.assertEqual(beta.respondent.headers['content-type'],
                         'text/event-stream')
        self.assertIn('transfer-encoding', beta.respondent.headers)
        self.assertEqual(beta.respondent.headers['transfer-encoding'],
                         'chunked')

        self.assertTrue(len(beta.events) >= 3)
        self.assertEqual(beta.respondent.retry, 1000)
        self.assertTrue(int(beta.respondent.leid) >= 2)
        event = beta.events.popleft()
        self.assertEqual(event, {'id': '0', 'name': '', 'data': 'START'})
        event = beta.events.popleft()
        self.assertEqual(event, {'id': '1', 'name': '', 'data': '1'})
        event = beta.events.popleft()
        self.assertEqual(event, {'id': '2', 'name': '', 'data': '2'})
        beta.events.clear()

        #keep going until ended
        timer.restart(duration=1.5)
        while (not timer.expired):
            alpha.serviceAll()
            time.sleep(0.05)
            beta.serviceAll()
            time.sleep(0.05)
            store.advanceStamp(0.1)

        self.assertTrue(len(beta.events) >= 3)
        self.assertEqual(beta.respondent.leid, '9')
        self.assertEqual(beta.events[-2], {'id': '9', 'name': '', 'data': '9'})
        self.assertEqual(beta.events[-1], {
            'id': '9',
            'name': '',
            'data': 'END'
        })
        beta.events.clear()

        alpha.servant.closeAll()
        beta.connector.close()

        wireLogAlpha.close()
        wireLogBeta.close()
Esempio n. 25
0
    def testTestStream(self):
        """
        Test Valet WSGI service request response stream sse
        """
        console.terse("{0}\n".format(self.testTestStream.__doc__))

        store = Store(stamp=0.0)

        app = bottle.default_app() # create bottle app

        ending.loadTest(app, store)

        console.terse("{0}\n".format("Building Valet ...\n"))
        wireLogAlpha = WireLog(buffify=True, same=True)
        result = wireLogAlpha.reopen()

        alpha = Valet(port = 6101,
                              bufsize=131072,
                              wlog=wireLogAlpha,
                              store=store,
                              app=app)
        self.assertIs(alpha.servant.reopen(), True)
        self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101))
        self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101))

        console.terse("{0}\n".format("Building Patron ...\n"))
        wireLogBeta = WireLog(buffify=True,  same=True)
        result = wireLogBeta.reopen()

        path = "http://{0}:{1}/".format('localhost', alpha.servant.eha[1])

        beta = Patron(bufsize=131072,
                                     wlog=wireLogBeta,
                                     store=store,
                                     path=path,
                                     reconnectable=True)

        self.assertIs(beta.connector.reopen(), True)
        self.assertIs(beta.connector.accepted, False)
        self.assertIs(beta.connector.connected, False)
        self.assertIs(beta.connector.cutoff, False)

        request = odict([('method', u'GET'),
                         ('path', u'/test/stream'),
                         ('qargs', odict()),
                         ('fragment', u''),
                         ('headers', odict([('Accept', 'application/json'),
                                            ('Content-Length', 0)])),
                         ('body', None),
                        ])

        beta.requests.append(request)
        timer = StoreTimer(store, duration=1.0)
        while (not timer.expired):
            alpha.serviceAll()
            time.sleep(0.05)
            beta.serviceAll()
            time.sleep(0.05)
            store.advanceStamp(0.1)

        self.assertIs(beta.connector.accepted, True)
        self.assertIs(beta.connector.connected, True)
        self.assertIs(beta.connector.cutoff, False)

        self.assertEqual(len(alpha.servant.ixes), 1)
        self.assertEqual(len(alpha.reqs), 1)
        self.assertEqual(len(alpha.reps), 1)
        requestant = alpha.reqs.values()[0]
        self.assertEqual(requestant.method, request['method'])
        self.assertEqual(requestant.url, request['path'])
        self.assertEqual(requestant.headers, {'accept': 'application/json',
                                                'accept-encoding': 'identity',
                                                'content-length': '0',
                                                'host': 'localhost:6101'})


        #timed out while stream still open so no responses in .responses
        self.assertIs(beta.waited, True)
        self.assertIs(beta.respondent.ended, False)
        self.assertEqual(len(beta.responses), 0)
        self.assertIn('content-type', beta.respondent.headers)
        self.assertEqual(beta.respondent.headers['content-type'], 'text/event-stream')
        self.assertIn('transfer-encoding', beta.respondent.headers)
        self.assertEqual(beta.respondent.headers['transfer-encoding'], 'chunked')

        self.assertTrue(len(beta.events) >= 3)
        self.assertEqual(beta.respondent.retry, 1000)
        self.assertTrue(int(beta.respondent.leid) >= 2)
        event = beta.events.popleft()
        self.assertEqual(event, {'id': '0', 'name': '', 'data': 'START'})
        event = beta.events.popleft()
        self.assertEqual(event, {'id': '1', 'name': '', 'data': '1'})
        event = beta.events.popleft()
        self.assertEqual(event, {'id': '2', 'name': '', 'data': '2'})
        beta.events.clear()

        #keep going until ended
        timer.restart(duration=1.5)
        while (not timer.expired):
            alpha.serviceAll()
            time.sleep(0.05)
            beta.serviceAll()
            time.sleep(0.05)
            store.advanceStamp(0.1)

        self.assertTrue(len(beta.events) >= 3)
        self.assertEqual(beta.respondent.leid,  '9')
        self.assertEqual(beta.events[-2], {'id': '9', 'name': '', 'data': '9'})
        self.assertEqual(beta.events[-1], {'id': '9', 'name': '', 'data': 'END'})
        beta.events.clear()

        alpha.servant.closeAll()
        beta.connector.close()

        wireLogAlpha.close()
        wireLogBeta.close()