Beispiel #1
0
    def setUp(self):
        self.oms_ssh = OmsShellProtocol()
        auth = getUtility(IAuthentication, context=None)
        user = auth.getPrincipal('user')
        user.groups.append('admins')
        self.oms_ssh.batch = True
        self.oms_ssh.logged_in(user)
        self.oms_ssh.batch = False

        self.terminal = mock.Mock()
        self.oms_ssh.terminal = self.terminal

        self.oms_ssh.connectionMade()

        # the standard model doesn't have any command or path which
        # is a prefix of another (len > 1), I don't want to force changes
        # to the model just for testing completion, so we have monkey patch
        # the commands() function and add a command 'hello'.
        self.orig_commands = registry.commands

        class HelloCmd(Cmd):
            name = 'hello'

        registry.commands = lambda: dict(hello=HelloCmd,
                                         **self.orig_commands())
    def setUp(self):
        self.oms_ssh = OmsShellProtocol()

        auth = getUtility(IAuthentication, context=None)
        user = auth.getPrincipal('user')
        user.groups.append('admins')
        self.oms_ssh.batch = True
        self.oms_ssh.logged_in(user)
        self.oms_ssh.batch = False

        self.oms_ssh.history_save_enabled = False

        self.terminal = mock.Mock()
        self.oms_ssh.terminal = self.terminal

        self.oms_ssh.enable_colors = False

        # Since uuids are random, static files/subdirectories like 'by-name'
        # subdir could be arbitrarily placed among them.
        # Let's monkeypatch the Container._new_id method to reasonably ensure
        # that created objects will always come first.
        # Order wasn't guaranted by OOBTree, now we sort in `ls` so this will work.
        self.old_new_id = Container._new_id
        Container._new_id = lambda self_: '0000_' + self.old_new_id(self_)
Beispiel #3
0
class OmsShellTerminalProtocol(object):
    """Connect a OmsShellProtocol to a web terminal session."""
    def logged_in(self, principal):
        self.principal = principal

    def connection_made(self, terminal, size):
        self.shell = OmsShellProtocol()
        self.shell.set_terminal(terminal)
        self.shell.connectionMade()
        self.shell.terminalSize(size[0], size[1])
        self.shell.logged_in(self.principal)

    def handle_key(self, key):
        self.shell.terminal.dataReceived(key)

    def terminalSize(self, width, height):
        # Insults terminal doesn't work well after resizes
        # also disabled in the oms shell over ssh.
        #
        # self.shell.terminalSize(width, height)
        pass
Beispiel #4
0
class OmsShellTerminalProtocol(object):
    """Connect a OmsShellProtocol to a web terminal session."""

    def logged_in(self, principal):
        self.principal = principal

    def connection_made(self, terminal, size):
        self.shell = OmsShellProtocol()
        self.shell.set_terminal(terminal)
        self.shell.connectionMade()
        self.shell.terminalSize(size[0], size[1])
        self.shell.logged_in(self.principal)

    def handle_key(self, key):
        self.shell.terminal.dataReceived(key)

    def terminalSize(self, width, height):
        # Insults terminal doesn't work well after resizes
        # also disabled in the oms shell over ssh.
        #
        # self.shell.terminalSize(width, height)
        pass
    def setUp(self):
        self.oms_ssh = OmsShellProtocol()
        auth = getUtility(IAuthentication, context=None)
        user = auth.getPrincipal('user')
        user.groups.append('admins')
        self.oms_ssh.batch = True
        self.oms_ssh.logged_in(user)
        self.oms_ssh.batch = False

        self.terminal = mock.Mock()
        self.oms_ssh.terminal = self.terminal

        self.oms_ssh.connectionMade()

        # the standard model doesn't have any command or path which
        # is a prefix of another (len > 1), I don't want to force changes
        # to the model just for testing completion, so we have monkey patch
        # the commands() function and add a command 'hello'.
        self.orig_commands = registry.commands

        class HelloCmd(Cmd):
            name = 'hello'
        registry.commands = lambda: dict(hello=HelloCmd, **self.orig_commands())
    def setUp(self):
        self.oms_ssh = OmsShellProtocol()

        auth = getUtility(IAuthentication, context=None)
        user = auth.getPrincipal('user')
        user.groups.append('admins')
        self.oms_ssh.batch = True
        self.oms_ssh.logged_in(user)
        self.oms_ssh.batch = False

        self.oms_ssh.history_save_enabled = False

        self.terminal = mock.Mock()
        self.oms_ssh.terminal = self.terminal

        self.oms_ssh.enable_colors = False

        # Since uuids are random, static files/subdirectories like 'by-name'
        # subdir could be arbitrarily placed among them.
        # Let's monkeypatch the Container._new_id method to reasonably ensure
        # that created objects will always come first.
        # Order wasn't guaranted by OOBTree, now we sort in `ls` so this will work.
        self.old_new_id = Container._new_id
        Container._new_id = lambda self_: '0000_' + self.old_new_id(self_)
Beispiel #7
0
 def connection_made(self, terminal, size):
     self.shell = OmsShellProtocol()
     self.shell.set_terminal(terminal)
     self.shell.connectionMade()
     self.shell.terminalSize(size[0], size[1])
     self.shell.logged_in(self.principal)
class SshTestCase(unittest.TestCase):

    tlds = ['bin', 'computes', 'machines', 'proc', 'search', 'stream']

    @run_in_reactor
    @clean_db
    def setUp(self):
        self.oms_ssh = OmsShellProtocol()

        auth = getUtility(IAuthentication, context=None)
        user = auth.getPrincipal('user')
        user.groups.append('admins')
        self.oms_ssh.batch = True
        self.oms_ssh.logged_in(user)
        self.oms_ssh.batch = False

        self.oms_ssh.history_save_enabled = False

        self.terminal = mock.Mock()
        self.oms_ssh.terminal = self.terminal

        self.oms_ssh.enable_colors = False

        # Since uuids are random, static files/subdirectories like 'by-name'
        # subdir could be arbitrarily placed among them.
        # Let's monkeypatch the Container._new_id method to reasonably ensure
        # that created objects will always come first.
        # Order wasn't guaranted by OOBTree, now we sort in `ls` so this will work.
        self.old_new_id = Container._new_id
        Container._new_id = lambda self_: '0000_' + self.old_new_id(self_)

    def tearDown(self):
        Container._new_id = self.old_new_id

    def _cmd(self, cmd):
        self.oms_ssh.lineReceived(cmd)

    def make_compute(self, hostname=u'tux-for-test', state=u'active', memory=2000):
        return Compute(hostname, state, memory)

    def test_quit(self):
        self._cmd('quit')
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('user@oms:/# ')

    def test_non_existent_cmd(self):
        self._cmd('non-existent-command')
        with assert_mock(self.terminal) as t:
            t.write('No such command: non-existent-command\n')
        assert not self.terminal.method_calls[1][1][0].startswith('Command returned an unhandled error')

    @run_in_reactor
    def test_pwd(self):
        self._cmd('pwd')
        with assert_mock(self.terminal) as t:
            t.write('/\n')

    @run_in_reactor
    def test_help(self):
        self._cmd('help')
        with assert_mock(self.terminal) as t:
            for cmd in commands().keys():
                assert cmd in current_call(t).arg

    @run_in_reactor
    def test_cd(self):
        for folder in self.tlds:
            for cmd in ['%s', '/%s', '//%s', '/./%s', '%s/.', '/%s/.']:
                self._cmd('cd %s' % (cmd % folder))
                assert self.oms_ssh._cwd() == '/%s' % folder

                self._cmd('cd ..')

    @run_in_reactor
    def test_cd_errors(self):
        computes = db.get_root()['oms_root']['computes']
        computes.add(self.make_compute())

        # TODO: reenable this when we'll have another leaf object.

        #self._cmd('cd /computes/%s' % cid)
        #with assert_mock(self.terminal) as t:
        #    t.write('Cannot cd to a non-container\n')

        #self.terminal.reset_mock()

        self._cmd('cd /nonexisting')
        with assert_mock(self.terminal) as t:
            t.write('No such object: /nonexisting\n')

    @run_in_reactor
    def test_cd_to_root(self):
        for cmd in ['cd', 'cd /', 'cd //', 'cd ../..', 'cd /..', 'cd']:
            self._cmd('cd computes')
            assert self.oms_ssh._cwd() == '/computes'
            self._cmd(cmd)
            assert self.oms_ssh._cwd() == '/'
            self.terminal.reset_mock()

    @run_in_reactor
    def test_ls(self):

        computes = db.get_root()['oms_root']['computes']
        computes.add(self.make_compute())

        # TODO: put back this when we find another leaf object

        #self.terminal.reset_mock()
        #self._cmd('ls /computes/%s' % cid)
        #with assert_mock(self.terminal) as t:
        #    t.write('/computes/%s\n' % cid)

        self.terminal.reset_mock()
        self._cmd('ls /computes/x')
        with assert_mock(self.terminal) as t:
            t.write('No such object: /computes/x\n')

    @run_in_reactor
    def test_ls_l(self):
        self.terminal.reset_mock()
        self._cmd('ls /computes -l')
        with assert_mock(self.terminal) as t:
            t.write('a---r-v-x root          <transient>         \tby-name/\t\n')
            skip(t, 1)
            no_more_calls(t)

        computes = db.get_root()['oms_root']['computes']
        compute = self.make_compute()
        cid = computes.add(compute)
        transaction.commit()

        self.terminal.reset_mock()
        self._cmd('ls /computes -l')
        with assert_mock(self.terminal) as t:
            t.write('a---r-v-x root %s\t%s@\t/machines/%s : tux-for-test\n' %
                    (datetime.datetime.fromtimestamp(compute.mtime).isoformat(), cid, cid))
            t.write('a---r-v-x root           <transient>        \tby-name/\t\n')
            skip(t, 1)
            no_more_calls(t)

    @run_in_reactor
    def test_cat_folders(self):
        for folder in self.tlds:
            self._cmd('cat %s' % folder)
            with assert_mock(self.terminal) as t:
                skip(t, 1)
                skip(t, 1)
                t.write("user@oms:/# ")
            self.terminal.reset_mock()

    @run_in_reactor
    def test_cat_compute(self):
        self._cmd('cat computes/1')
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/1\n")
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/1\n")

        self.terminal.reset_mock()

        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write('Host name:             tux-for-test\n')
            whatever(t)
            t.write('Architecture:          x86_64, linux, centos\n')
            whatever(t)
            t.write('State:                 active\n')
            whatever(t)
            t.write('RAM Size:              2000\n')

    @run_in_reactor
    def test_cat_l_compute(self):
        self.terminal.reset_mock()

        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('cat -l computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Architecture:          x86_64\n'
                    '                       linux\n'
                    '                       centos\n')
            whatever(t)
            t.write('Diskspace Utilization: boot: 49.3\n'
                    '                       storage: 748.3\n'
                    '                       root: 249.0\n')

    @run_in_reactor
    def test_rm_compute(self):
        self._cmd('cat computes/1')
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/1\n")

        self.terminal.reset_mock()

        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('cat computes/%s' % cid)

        with assert_mock(self.terminal) as t:
            t.write('Host name:             tux-for-test\n')
            whatever(t)
            t.write('Architecture:          x86_64, linux, centos\n')
            whatever(t)
            t.write('State:                 active\n')
            whatever(t)
            t.write('RAM Size:              2000\n')

        self._cmd('rm computes/%s' % cid)

        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/%s\n" % cid)

        self.terminal.reset_mock()

        self._cmd('rm computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/%s\n" % cid)

    @run_in_reactor
    def test_move_compute(self):
        class ITest(Interface):
            pass

        class TestContainer(Container):
            __contains__ = Compute

        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        orig_current_object = MoveCmd.current_obj

        try:
            MoveCmd.current_obj = TestContainer()
            self._cmd('mv /computes/%s .' % cid)
            eq_(len(MoveCmd.current_obj._items), 1)
        finally:
            MoveCmd.current_obj = orig_current_object

    @run_in_reactor
    def test_rename_compute(self):
        computes = db.get_root()['oms_root']['computes']
        compute = self.make_compute()
        cid = computes.add(compute)
        transaction.commit()

        self._cmd('mv /machines/%s /machines/123' % cid)
        eq_(compute.__name__, '123')

        self.terminal.reset_mock()

        self._cmd('cat /machines/123')
        with assert_mock(self.terminal) as t:
            t.write('Host name:             tux-for-test\n')

    @run_in_reactor
    def test_modify_compute(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('set computes/%s hostname=TUX-FOR-TEST' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write('Host name:             TUX-FOR-TEST\n')
            whatever(t)
            t.write('Architecture:          x86_64, linux, centos\n')
            whatever(t)
            t.write('State:                 active\n')
            whatever(t)
            t.write('RAM Size:              2000\n')

        self.terminal.reset_mock()
        self._cmd('set computes/123')
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/123\n")

        self.terminal.reset_mock()
        self._cmd('set computes')
        with assert_mock(self.terminal) as t:
            t.write("No schema found for object\n")

    @run_in_reactor
    def test_modify_compute_verbose(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('set computes/%s hostname=TUX-FOR-TEST -v' % cid)
        with assert_mock(self.terminal) as t:
            t.write("Setting hostname=TUX-FOR-TEST\n")

        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write('Host name:             TUX-FOR-TEST\n')

    @run_in_reactor
    def test_modify_compute_errors(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('set computes/%s hostname=x' % cid)
        with assert_mock(self.terminal) as t:
            t.write("hostname: Value is too short\n")

    @run_in_reactor
    def test_modify_compute_tags(self):
        computes = db.get_root()['oms_root']['computes']
        cmpt = self.make_compute()
        cid = computes.add(cmpt)
        transaction.commit()

        self._cmd('set computes/%s tags=taga,tagb' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Tags:                  arch:centos, arch:linux, arch:x86_64, label:taga, label:tagb, state:active, type:compute\n')

        self._cmd('set computes/%s tags=taga,-tagb' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Tags:                  arch:centos, arch:linux, arch:x86_64, label:taga, state:active, type:compute\n')

    @run_in_reactor
    def test_special_compute_tags(self):
        computes = db.get_root()['oms_root']['computes']
        cmpt = self.make_compute()
        cid = computes.add(cmpt)
        transaction.commit()

        self._cmd('set computes/%s tags=foo' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Tags:                  arch:centos, arch:linux, arch:x86_64, label:foo, state:active, type:compute\n')

        self._cmd('set computes/%s tags=label:foo' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Tags:                  arch:centos, arch:linux, arch:x86_64, label:foo, state:active, type:compute\n')

        self._cmd('set computes/%s tags=+type:foo' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Tags:                  arch:centos, arch:linux, arch:x86_64, label:foo, state:active, type:compute\n')

        self._cmd('set computes/%s tags="+space: ship"' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Tags:                  arch:centos, arch:linux, arch:x86_64, label:foo, space:ship, state:active, type:compute\n')

        self._cmd('set computes/%s tags=stuff:' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Tags:                  arch:centos, arch:linux, arch:x86_64, state:active, type:compute\n')

        self._cmd('set computes/%s tags=:stuff' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Tags:                  arch:centos, arch:linux, arch:x86_64, state:active, type:compute\n')

        self._cmd('set computes/%s tags=,,' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Tags:                  arch:centos, arch:linux, arch:x86_64, state:active, type:compute\n')

    @run_in_reactor
    def test_create_compute(self):
        self._cmd("cd /computes")
        self._cmd("mk compute hostname=TUX-FOR-TEST memory=2000 state=active")
        cid = self.terminal.method_calls[-2][1][0]

        self.terminal.reset_mock()
        self._cmd('cat %s' % cid)

        with assert_mock(self.terminal) as t:
            t.write('Host name:             TUX-FOR-TEST\n')
            whatever(t)
            t.write('Architecture:          x86_64, linux, centos\n')
            whatever(t)
            t.write('State:                 active\n')
            whatever(t)
            t.write('RAM Size:              2000\n')

    @run_in_reactor
    def test_create_compute_mandatory_args(self):
        self._cmd("cd /computes")

        self.terminal.reset_mock()
        self._cmd("mk compute hostname=TUX-FOR-TEST memory=2000")

        with assert_mock(self.terminal) as t:
            t.write("argument =state is required")

    @run_in_reactor
    def test_create_compute_invalid_args(self):
        self._cmd("cd /computes")

        self.terminal.reset_mock()
        self._cmd("mk compute hostname=x memory=2 state=active")

        with assert_mock(self.terminal) as t:
            t.write("hostname: Value is too short\n")

    @run_in_reactor
    def test_mk_keyword_declaration(self):
        class ITest(Interface):
            attr = zope.schema.TextLine(title=u"Test")

        class Test(Model):
            implements(ITest)

            # The optional arg is important for this test.
            #
            # CreateObjCmd will try to get the value of keyword switches for each
            # parameter of the model constructor, including default ones.
            # However if this optional is not defined in the schema,
            # the argument parser will not contain any argument definition for
            # the 'keywords' option, so the 'keywords' arg object attribute
            # will not be define unless explicitly declared with `arg_declare`.
            def __init__(self, some_optional=None):
                pass

        class TestContainer(Container):
            __contains__ = Test

            added = False

            def add(self, item):
                self.added = True

        creatable_models['some-test'] = Test
        orig_current_object = CreateObjCmd.current_obj

        try:
            CreateObjCmd.current_obj = TestContainer()
            self._cmd('mk some-test attr=value')
            assert CreateObjCmd.current_obj.added
        finally:
            CreateObjCmd.current_obj = orig_current_object
            del creatable_models['some-test']

    @run_in_reactor
    def test_create_multiple_interfaces(self):
        class ITestA(Interface):
            architecture = schema.Choice(title=u"Architecture", values=(u'linux', u'win32', u'darwin', u'bsd', u'solaris'))

        class ITestB(Interface):
            state = schema.Choice(title=u"State", values=(u'active', u'inactive', u'standby'))

        class Test(Model):
            implements(ITestA, ITestB)

            def __init__(self, *args):
                pass

        class TestContainer(Container):
            __contains__ = Test

            added = False

            def add(self, item):
                self.added = True

        creatable_models['some-test'] = Test
        orig_current_object = CreateObjCmd.current_obj

        try:
            CreateObjCmd.current_obj = TestContainer()
            self._cmd('mk some-test architecture=linux state=active')
            assert CreateObjCmd.current_obj.added
        finally:
            CreateObjCmd.current_obj = orig_current_object
            del creatable_models['some-test']

    @run_in_reactor
    def test_context_dependent_help(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self.terminal.reset_mock()
        self._cmd('set computes/%s -h' % cid)

        with assert_mock(self.terminal) as t:
            assert 'hostname=' in current_call(t).arg

    @run_in_reactor
    def test_parsing_error_message(self):
        self._cmd('mk unknown')
        eq_(len(self.terminal.method_calls), 3)

    @run_in_reactor
    def test_last_error(self):
        class meta(FakeModule):
            class FailingCommand(Cmd):
                command('fail')

                def execute(self, args):
                    raise Exception('some mock error')

        grok('martiantest.fake.meta')

        self._cmd('fail')
        self.terminal.reset_mock()
        self._cmd('last_error')
        with assert_mock(self.terminal) as t:
            assert 'some mock error' in current_call(t).arg, 'Apparently, fail command did not fail'

    @run_in_reactor
    def test_suggestion(self):
        self._cmd('make')
        with assert_mock(self.terminal) as t:
            skip(t, 1)
            t.write("Do you mean 'mk'?\n")

    @run_in_reactor
    def test_wildcard(self):
        self._cmd('echo /c*')
        with assert_mock(self.terminal) as t:
            t.write('/computes\n')

        self.terminal.reset_mock()

        self._cmd('echo /[cx]omputes')
        with assert_mock(self.terminal) as t:
            t.write('/computes\n')

        self.terminal.reset_mock()
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('echo /computes/*-[a-z0-9]*-*')
        with assert_mock(self.terminal) as t:
            t.write('/computes/%s\n' % (cid))

    @run_in_reactor
    def test_cmd_path(self):
        self._cmd('/bin/echo test')
        with assert_mock(self.terminal) as t:
            t.write('test\n')

        self.terminal.reset_mock()
        self._cmd('bin/echo test')
        with assert_mock(self.terminal) as t:
            t.write('test\n')

        self._cmd('cd computes')
        self.terminal.reset_mock()
        self._cmd('../bin/echo test')
        with assert_mock(self.terminal) as t:
            t.write('test\n')

    @run_in_reactor
    def test_acl(self):
        self._cmd('setfacl / -m u:user:r')

        self.terminal.reset_mock()
        self._cmd('getfacl /')
        with assert_mock(self.terminal) as t:
            t.write('user:user:+r\n')

        self._cmd('setfacl / -m u:user:w')

        self.terminal.reset_mock()
        self._cmd('getfacl /')
        with assert_mock(self.terminal) as t:
            t.write('user:user:+rw\n')

        self._cmd('setfacl / -d u:user:a')

        self.terminal.reset_mock()
        self._cmd('getfacl /')
        with assert_mock(self.terminal) as t:
            t.write('user:user:+rw\n')
            t.write('user:user:-a\n')

        self._cmd('setfacl / -d u:user:w')

        self.terminal.reset_mock()
        self._cmd('getfacl /')
        with assert_mock(self.terminal) as t:
            t.write('user:user:+r\n')
            t.write('user:user:-aw\n')

        self.terminal.reset_mock()
        self._cmd('setfacl / -d u:user:G')
        with assert_mock(self.terminal) as t:
            t.write("No such permission 'G'\n")

    def test_tokenizer(self):
        arglist = r'set /computes/some\ file\ \ with\ spaces -v --help key=value other_key="quoted value" "lastkey"="escaped \" quotes"'

        eq_(self.oms_ssh.tokenizer.tokenize(arglist),
                ['set', '/computes/some file  with spaces', '-v', '--help', '=key', 'value', '=other_key', 'quoted value', '=lastkey', 'escaped " quotes'])

        with assert_raises(CommandLineSyntaxError):
            self.oms_ssh.tokenizer.tokenize('ls " -l')

        arglist = r'set test cornercase="glued""quoted"'
        eq_(self.oms_ssh.tokenizer.tokenize(arglist),
                ['set', 'test', '=cornercase', 'gluedquoted'])

        arglist = r'set test # comment'
        eq_(self.oms_ssh.tokenizer.tokenize(arglist),
                ['set', 'test'])
Beispiel #9
0
class CmdCompletionTestCase(unittest.TestCase):
    def setUp(self):
        self.oms_ssh = OmsShellProtocol()
        auth = getUtility(IAuthentication, context=None)
        user = auth.getPrincipal('user')
        user.groups.append('admins')
        self.oms_ssh.batch = True
        self.oms_ssh.logged_in(user)
        self.oms_ssh.batch = False

        self.terminal = mock.Mock()
        self.oms_ssh.terminal = self.terminal

        self.oms_ssh.connectionMade()

        # the standard model doesn't have any command or path which
        # is a prefix of another (len > 1), I don't want to force changes
        # to the model just for testing completion, so we have monkey patch
        # the commands() function and add a command 'hello'.
        self.orig_commands = registry.commands

        class HelloCmd(Cmd):
            name = 'hello'

        registry.commands = lambda: dict(hello=HelloCmd,
                                         **self.orig_commands())

    def tearDown(self):
        registry.commands = self.orig_commands

    def make_compute(self,
                     hostname=u'tux-for-test',
                     state=u'active',
                     arch=u'linux',
                     memory=2000):
        return Compute(hostname, state, arch, memory)

    def _input(self, string):
        for s in string:
            self.oms_ssh.characterReceived(s, False)

    def _tab_after(self, string):
        self._input(string)
        self.terminal.reset_mock()

        self.oms_ssh.handle_TAB()

    def test_command_completion(self):
        self._tab_after('q')
        with assert_mock(self.terminal) as t:
            t.write('uit ')
            no_more_calls(t)

    def test_command_completion_spaces(self):
        self._tab_after('    q')
        with assert_mock(self.terminal) as t:
            t.write('uit ')
            no_more_calls(t)

    def test_complete_not_found(self):
        self._tab_after('asdasd')
        with assert_mock(self.terminal) as t:
            no_more_calls(t)

    def test_complete_quotes(self):
        self._tab_after('ls "comp')
        with assert_mock(self.terminal) as t:
            t.write('utes/')
            no_more_calls(t)

    def test_complete_prefix(self):
        self._tab_after('he')
        with assert_mock(self.terminal) as t:
            t.write('l')
            no_more_calls(t)

        # hit tab twice
        self.terminal.reset_mock()
        self.oms_ssh.handle_TAB()

        with assert_mock(self.terminal) as t:
            t.write('')
            t.nextLine()
            t.write('help  hello\n')
            t.write(self.oms_ssh.ps[0] + 'hel')
            no_more_calls(t)

    def test_spaces_between_arg(self):
        self._tab_after('ls comp')

        with assert_mock(self.terminal) as t:
            t.write('utes/')
            no_more_calls(t)

    def test_command_arg_spaces_before_command(self):
        self._tab_after(' ls comp')
        with assert_mock(self.terminal) as t:
            t.write('utes/')
            no_more_calls(t)

    def test_mandatory_positional(self):
        self._tab_after('cat ')
        with assert_mock(self.terminal) as t:
            skip(t, 4)
            no_more_calls(t)

    def test_complete_switches(self):
        self._tab_after('quit ')
        with assert_mock(self.terminal) as t:
            no_more_calls(t)

        # hit tab twice
        self.oms_ssh.handle_TAB()
        with assert_mock(self.terminal) as t:
            no_more_calls(t)

        # now try with a dash
        self._tab_after('-')
        with assert_mock(self.terminal) as t:
            t.write('')
            t.nextLine()
            t.write('-h  --help\n')
            t.write(self.oms_ssh.ps[0] + 'quit -')
            no_more_calls(t)
        # disambiguate
        self._tab_after('-')
        with assert_mock(self.terminal) as t:
            t.write('help ')
            no_more_calls(t)

    def test_complete_consumed_switches(self):
        self._tab_after('ls --help')
        with assert_mock(self.terminal) as t:
            t.write(' ')
            no_more_calls(t)

        self._tab_after('-')
        with assert_mock(self.terminal) as t:
            skip(t, 2)
            with current_call(t) as c:
                assert 'help' not in c.arg
                assert '-h' not in c.arg

    @run_in_reactor
    def test_complete_context_dependent_no_context(self):
        """Test whether context dependent arguments are correctly built when the
        context argument (i.e. the `set` cmd `path` argument) is not yet present.

        """
        self._tab_after('set /comp')
        with assert_mock(self.terminal) as t:
            t.write('utes/')
            no_more_calls(t)

    @run_in_reactor
    def test_complete_keyword_switches(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._tab_after('set /computes/%s st' % cid)
        with assert_mock(self.terminal) as t:
            t.write('ate=')
            no_more_calls(t)

        self._tab_after('ina')
        with assert_mock(self.terminal) as t:
            t.write('ctive ')
            no_more_calls(t)

    @run_in_reactor
    def test_complete_keyword_switches_mk(self):
        self.oms_ssh.lineReceived('cd computes')

        self._tab_after('mk compute st')
        with assert_mock(self.terminal) as t:
            t.write('ate=')
            no_more_calls(t)

        self._tab_after('ina')
        with assert_mock(self.terminal) as t:
            t.write('ctive ')
            no_more_calls(t)

    @run_in_reactor
    def test_complete_consumed_keyword_switches_mk(self):
        """Test consuming of already completed switches when there are mandatory arguments."""
        self.oms_ssh.lineReceived('cd computes')

        self._tab_after('mk compute st')
        with assert_mock(self.terminal) as t:
            t.write('ate=')
            no_more_calls(t)

        self._tab_after('ina')
        with assert_mock(self.terminal) as t:
            t.write('ctive ')
            no_more_calls(t)

        self._tab_after('st')
        assert not self.terminal.method_calls

    @run_in_reactor
    def test_complete_mk_legal_types(self):
        """Test that only legal types are shown."""
        self.oms_ssh.lineReceived('cd computes')

        self._tab_after('mk net')
        assert not self.terminal.method_calls

        self.oms_ssh.handle_RETURN()
        self.terminal.reset_mock()

        self._tab_after('mk comp')
        #~ eq_(self.terminal.method_calls, [('write', ('ute ',), {})])
        with assert_mock(self.terminal) as t:
            t.write('ute ')
            no_more_calls(t)

        self._tab_after('arch')
        with assert_mock(self.terminal) as t:
            t.write('itecture=')
            no_more_calls(t)

    @run_in_reactor
    def test_complete_mk_legal_types_interface(self):
        class ITest(Interface):
            pass

        class Test(Model):
            implements(ITest)

            def __init__(self):
                pass

        class TestInterfaceContainer(Container):
            __contains__ = ITest

        class TestClassContainer(Container):
            __contains__ = Test

        creatable_models['some-test'] = Test

        orig_current_object = commands.CreateObjCmd.current_obj

        try:
            commands.CreateObjCmd.current_obj = TestInterfaceContainer()
            self._tab_after('mk ')
            with assert_mock(self.terminal) as t:
                t.write('some-test ')
                no_more_calls(t)

            self.oms_ssh.handle_RETURN()
            self.terminal.reset_mock()

            commands.CreateObjCmd.current_obj = TestClassContainer()

            self._tab_after('mk ')
            with assert_mock(self.terminal) as t:
                t.write('some-test ')
                no_more_calls(t)
        finally:
            commands.CreateObjCmd.current_obj = orig_current_object
            del creatable_models['some-test']

    @run_in_reactor
    def test_complete_positional_choice(self):
        self.oms_ssh.lineReceived('cd computes')

        self._tab_after('mk comp')
        with assert_mock(self.terminal) as t:
            t.write('ute ')
            no_more_calls(t)

        self._tab_after('comp')
        assert not self.terminal.method_calls

    @run_in_reactor
    def test_complete_container_symlink(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._tab_after('cd /computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write('/')
class SshTestCase(unittest.TestCase):

    tlds = ['bin', 'computes', 'machines', 'proc', 'search', 'stream']

    @run_in_reactor
    @clean_db
    def setUp(self):
        self.oms_ssh = OmsShellProtocol()

        auth = getUtility(IAuthentication, context=None)
        user = auth.getPrincipal('user')
        user.groups.append('admins')
        self.oms_ssh.batch = True
        self.oms_ssh.logged_in(user)
        self.oms_ssh.batch = False

        self.oms_ssh.history_save_enabled = False

        self.terminal = mock.Mock()
        self.oms_ssh.terminal = self.terminal

        self.oms_ssh.enable_colors = False

        # Since uuids are random, static files/subdirectories like 'by-name'
        # subdir could be arbitrarily placed among them.
        # Let's monkeypatch the Container._new_id method to reasonably ensure
        # that created objects will always come first.
        # Order wasn't guaranted by OOBTree, now we sort in `ls` so this will work.
        self.old_new_id = Container._new_id
        Container._new_id = lambda self_: '0000_' + self.old_new_id(self_)

    def tearDown(self):
        Container._new_id = self.old_new_id

    def _cmd(self, cmd):
        self.oms_ssh.lineReceived(cmd)

    def make_compute(self,
                     hostname=u'tux-for-test',
                     state=u'active',
                     memory=2000):
        return Compute(hostname, state, memory)

    def test_quit(self):
        self._cmd('quit')
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('user@oms:/# ')

    def test_non_existent_cmd(self):
        self._cmd('non-existent-command')
        with assert_mock(self.terminal) as t:
            t.write('No such command: non-existent-command\n')
        assert not self.terminal.method_calls[1][1][0].startswith(
            'Command returned an unhandled error')

    @run_in_reactor
    def test_pwd(self):
        self._cmd('pwd')
        with assert_mock(self.terminal) as t:
            t.write('/\n')

    @run_in_reactor
    def test_help(self):
        self._cmd('help')
        with assert_mock(self.terminal) as t:
            for cmd in commands().keys():
                assert cmd in current_call(t).arg

    @run_in_reactor
    def test_cd(self):
        for folder in self.tlds:
            for cmd in ['%s', '/%s', '//%s', '/./%s', '%s/.', '/%s/.']:
                self._cmd('cd %s' % (cmd % folder))
                assert self.oms_ssh._cwd() == '/%s' % folder

                self._cmd('cd ..')

    @run_in_reactor
    def test_cd_errors(self):
        computes = db.get_root()['oms_root']['computes']
        computes.add(self.make_compute())

        # TODO: reenable this when we'll have another leaf object.

        #self._cmd('cd /computes/%s' % cid)
        #with assert_mock(self.terminal) as t:
        #    t.write('Cannot cd to a non-container\n')

        #self.terminal.reset_mock()

        self._cmd('cd /nonexisting')
        with assert_mock(self.terminal) as t:
            t.write('No such object: /nonexisting\n')

    @run_in_reactor
    def test_cd_to_root(self):
        for cmd in ['cd', 'cd /', 'cd //', 'cd ../..', 'cd /..', 'cd']:
            self._cmd('cd computes')
            assert self.oms_ssh._cwd() == '/computes'
            self._cmd(cmd)
            assert self.oms_ssh._cwd() == '/'
            self.terminal.reset_mock()

    @run_in_reactor
    def test_ls(self):

        computes = db.get_root()['oms_root']['computes']
        computes.add(self.make_compute())

        # TODO: put back this when we find another leaf object

        #self.terminal.reset_mock()
        #self._cmd('ls /computes/%s' % cid)
        #with assert_mock(self.terminal) as t:
        #    t.write('/computes/%s\n' % cid)

        self.terminal.reset_mock()
        self._cmd('ls /computes/x')
        with assert_mock(self.terminal) as t:
            t.write('No such object: /computes/x\n')

    @run_in_reactor
    def test_ls_l(self):
        self.terminal.reset_mock()
        self._cmd('ls /computes -l')
        with assert_mock(self.terminal) as t:
            t.write(
                'a---r-v-x root          <transient>         \tby-name/\t\n')
            skip(t, 1)
            no_more_calls(t)

        computes = db.get_root()['oms_root']['computes']
        compute = self.make_compute()
        cid = computes.add(compute)
        transaction.commit()

        self.terminal.reset_mock()
        self._cmd('ls /computes -l')
        with assert_mock(self.terminal) as t:
            t.write('a---r-v-x root %s\t%s@\t/machines/%s : tux-for-test\n' %
                    (datetime.datetime.fromtimestamp(
                        compute.mtime).isoformat(), cid, cid))
            t.write(
                'a---r-v-x root           <transient>        \tby-name/\t\n')
            skip(t, 1)
            no_more_calls(t)

    @run_in_reactor
    def test_cat_folders(self):
        for folder in self.tlds:
            self._cmd('cat %s' % folder)
            with assert_mock(self.terminal) as t:
                skip(t, 1)
                skip(t, 1)
                t.write("user@oms:/# ")
            self.terminal.reset_mock()

    @run_in_reactor
    def test_cat_compute(self):
        self._cmd('cat computes/1')
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/1\n")
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/1\n")

        self.terminal.reset_mock()

        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write('Host name:             tux-for-test\n')
            whatever(t)
            t.write('Architecture:          x86_64, linux, centos\n')
            whatever(t)
            t.write('State:                 active\n')
            whatever(t)
            t.write('RAM Size:              2000\n')

    @run_in_reactor
    def test_cat_l_compute(self):
        self.terminal.reset_mock()

        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('cat -l computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write('Architecture:          x86_64\n'
                    '                       linux\n'
                    '                       centos\n')
            whatever(t)
            t.write('Diskspace Utilization: boot: 49.3\n'
                    '                       storage: 748.3\n'
                    '                       root: 249.0\n')

    @run_in_reactor
    def test_rm_compute(self):
        self._cmd('cat computes/1')
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/1\n")

        self.terminal.reset_mock()

        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('cat computes/%s' % cid)

        with assert_mock(self.terminal) as t:
            t.write('Host name:             tux-for-test\n')
            whatever(t)
            t.write('Architecture:          x86_64, linux, centos\n')
            whatever(t)
            t.write('State:                 active\n')
            whatever(t)
            t.write('RAM Size:              2000\n')

        self._cmd('rm computes/%s' % cid)

        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/%s\n" % cid)

        self.terminal.reset_mock()

        self._cmd('rm computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/%s\n" % cid)

    @run_in_reactor
    def test_move_compute(self):
        class ITest(Interface):
            pass

        class TestContainer(Container):
            __contains__ = Compute

        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        orig_current_object = MoveCmd.current_obj

        try:
            MoveCmd.current_obj = TestContainer()
            self._cmd('mv /computes/%s .' % cid)
            eq_(len(MoveCmd.current_obj._items), 1)
        finally:
            MoveCmd.current_obj = orig_current_object

    @run_in_reactor
    def test_rename_compute(self):
        computes = db.get_root()['oms_root']['computes']
        compute = self.make_compute()
        cid = computes.add(compute)
        transaction.commit()

        self._cmd('mv /machines/%s /machines/123' % cid)
        eq_(compute.__name__, '123')

        self.terminal.reset_mock()

        self._cmd('cat /machines/123')
        with assert_mock(self.terminal) as t:
            t.write('Host name:             tux-for-test\n')

    @run_in_reactor
    def test_modify_compute(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('set computes/%s hostname=TUX-FOR-TEST' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write('Host name:             TUX-FOR-TEST\n')
            whatever(t)
            t.write('Architecture:          x86_64, linux, centos\n')
            whatever(t)
            t.write('State:                 active\n')
            whatever(t)
            t.write('RAM Size:              2000\n')

        self.terminal.reset_mock()
        self._cmd('set computes/123')
        with assert_mock(self.terminal) as t:
            t.write("No such object: computes/123\n")

        self.terminal.reset_mock()
        self._cmd('set computes')
        with assert_mock(self.terminal) as t:
            t.write("No schema found for object\n")

    @run_in_reactor
    def test_modify_compute_verbose(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('set computes/%s hostname=TUX-FOR-TEST -v' % cid)
        with assert_mock(self.terminal) as t:
            t.write("Setting hostname=TUX-FOR-TEST\n")

        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write('Host name:             TUX-FOR-TEST\n')

    @run_in_reactor
    def test_modify_compute_errors(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('set computes/%s hostname=x' % cid)
        with assert_mock(self.terminal) as t:
            t.write("hostname: Value is too short\n")

    @run_in_reactor
    def test_modify_compute_tags(self):
        computes = db.get_root()['oms_root']['computes']
        cmpt = self.make_compute()
        cid = computes.add(cmpt)
        transaction.commit()

        self._cmd('set computes/%s tags=taga,tagb' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write(
                'Tags:                  arch:centos, arch:linux, arch:x86_64, label:taga, label:tagb, state:active, type:compute\n'
            )

        self._cmd('set computes/%s tags=taga,-tagb' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write(
                'Tags:                  arch:centos, arch:linux, arch:x86_64, label:taga, state:active, type:compute\n'
            )

    @run_in_reactor
    def test_special_compute_tags(self):
        computes = db.get_root()['oms_root']['computes']
        cmpt = self.make_compute()
        cid = computes.add(cmpt)
        transaction.commit()

        self._cmd('set computes/%s tags=foo' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write(
                'Tags:                  arch:centos, arch:linux, arch:x86_64, label:foo, state:active, type:compute\n'
            )

        self._cmd('set computes/%s tags=label:foo' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write(
                'Tags:                  arch:centos, arch:linux, arch:x86_64, label:foo, state:active, type:compute\n'
            )

        self._cmd('set computes/%s tags=+type:foo' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write(
                'Tags:                  arch:centos, arch:linux, arch:x86_64, label:foo, state:active, type:compute\n'
            )

        self._cmd('set computes/%s tags="+space: ship"' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write(
                'Tags:                  arch:centos, arch:linux, arch:x86_64, label:foo, space:ship, state:active, type:compute\n'
            )

        self._cmd('set computes/%s tags=stuff:' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write(
                'Tags:                  arch:centos, arch:linux, arch:x86_64, state:active, type:compute\n'
            )

        self._cmd('set computes/%s tags=:stuff' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write(
                'Tags:                  arch:centos, arch:linux, arch:x86_64, state:active, type:compute\n'
            )

        self._cmd('set computes/%s tags=,,' % cid)
        self.terminal.reset_mock()

        self._cmd('cat computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            whatever(t)
            t.write(
                'Tags:                  arch:centos, arch:linux, arch:x86_64, state:active, type:compute\n'
            )

    @run_in_reactor
    def test_create_compute(self):
        self._cmd("cd /computes")
        self._cmd("mk compute hostname=TUX-FOR-TEST memory=2000 state=active")
        cid = self.terminal.method_calls[-2][1][0]

        self.terminal.reset_mock()
        self._cmd('cat %s' % cid)

        with assert_mock(self.terminal) as t:
            t.write('Host name:             TUX-FOR-TEST\n')
            whatever(t)
            t.write('Architecture:          x86_64, linux, centos\n')
            whatever(t)
            t.write('State:                 active\n')
            whatever(t)
            t.write('RAM Size:              2000\n')

    @run_in_reactor
    def test_create_compute_mandatory_args(self):
        self._cmd("cd /computes")

        self.terminal.reset_mock()
        self._cmd("mk compute hostname=TUX-FOR-TEST memory=2000")

        with assert_mock(self.terminal) as t:
            t.write("argument =state is required")

    @run_in_reactor
    def test_create_compute_invalid_args(self):
        self._cmd("cd /computes")

        self.terminal.reset_mock()
        self._cmd("mk compute hostname=x memory=2 state=active")

        with assert_mock(self.terminal) as t:
            t.write("hostname: Value is too short\n")

    @run_in_reactor
    def test_mk_keyword_declaration(self):
        class ITest(Interface):
            attr = zope.schema.TextLine(title=u"Test")

        class Test(Model):
            implements(ITest)

            # The optional arg is important for this test.
            #
            # CreateObjCmd will try to get the value of keyword switches for each
            # parameter of the model constructor, including default ones.
            # However if this optional is not defined in the schema,
            # the argument parser will not contain any argument definition for
            # the 'keywords' option, so the 'keywords' arg object attribute
            # will not be define unless explicitly declared with `arg_declare`.
            def __init__(self, some_optional=None):
                pass

        class TestContainer(Container):
            __contains__ = Test

            added = False

            def add(self, item):
                self.added = True

        creatable_models['some-test'] = Test
        orig_current_object = CreateObjCmd.current_obj

        try:
            CreateObjCmd.current_obj = TestContainer()
            self._cmd('mk some-test attr=value')
            assert CreateObjCmd.current_obj.added
        finally:
            CreateObjCmd.current_obj = orig_current_object
            del creatable_models['some-test']

    @run_in_reactor
    def test_create_multiple_interfaces(self):
        class ITestA(Interface):
            architecture = schema.Choice(title=u"Architecture",
                                         values=(u'linux', u'win32', u'darwin',
                                                 u'bsd', u'solaris'))

        class ITestB(Interface):
            state = schema.Choice(title=u"State",
                                  values=(u'active', u'inactive', u'standby'))

        class Test(Model):
            implements(ITestA, ITestB)

            def __init__(self, *args):
                pass

        class TestContainer(Container):
            __contains__ = Test

            added = False

            def add(self, item):
                self.added = True

        creatable_models['some-test'] = Test
        orig_current_object = CreateObjCmd.current_obj

        try:
            CreateObjCmd.current_obj = TestContainer()
            self._cmd('mk some-test architecture=linux state=active')
            assert CreateObjCmd.current_obj.added
        finally:
            CreateObjCmd.current_obj = orig_current_object
            del creatable_models['some-test']

    @run_in_reactor
    def test_context_dependent_help(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self.terminal.reset_mock()
        self._cmd('set computes/%s -h' % cid)

        with assert_mock(self.terminal) as t:
            assert 'hostname=' in current_call(t).arg

    @run_in_reactor
    def test_parsing_error_message(self):
        self._cmd('mk unknown')
        eq_(len(self.terminal.method_calls), 3)

    @run_in_reactor
    def test_last_error(self):
        class meta(FakeModule):
            class FailingCommand(Cmd):
                command('fail')

                def execute(self, args):
                    raise Exception('some mock error')

        grok('martiantest.fake.meta')

        self._cmd('fail')
        self.terminal.reset_mock()
        self._cmd('last_error')
        with assert_mock(self.terminal) as t:
            assert 'some mock error' in current_call(
                t).arg, 'Apparently, fail command did not fail'

    @run_in_reactor
    def test_suggestion(self):
        self._cmd('make')
        with assert_mock(self.terminal) as t:
            skip(t, 1)
            t.write("Do you mean 'mk'?\n")

    @run_in_reactor
    def test_wildcard(self):
        self._cmd('echo /c*')
        with assert_mock(self.terminal) as t:
            t.write('/computes\n')

        self.terminal.reset_mock()

        self._cmd('echo /[cx]omputes')
        with assert_mock(self.terminal) as t:
            t.write('/computes\n')

        self.terminal.reset_mock()
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._cmd('echo /computes/*-[a-z0-9]*-*')
        with assert_mock(self.terminal) as t:
            t.write('/computes/%s\n' % (cid))

    @run_in_reactor
    def test_cmd_path(self):
        self._cmd('/bin/echo test')
        with assert_mock(self.terminal) as t:
            t.write('test\n')

        self.terminal.reset_mock()
        self._cmd('bin/echo test')
        with assert_mock(self.terminal) as t:
            t.write('test\n')

        self._cmd('cd computes')
        self.terminal.reset_mock()
        self._cmd('../bin/echo test')
        with assert_mock(self.terminal) as t:
            t.write('test\n')

    @run_in_reactor
    def test_acl(self):
        self._cmd('setfacl / -m u:user:r')

        self.terminal.reset_mock()
        self._cmd('getfacl /')
        with assert_mock(self.terminal) as t:
            t.write('user:user:+r\n')

        self._cmd('setfacl / -m u:user:w')

        self.terminal.reset_mock()
        self._cmd('getfacl /')
        with assert_mock(self.terminal) as t:
            t.write('user:user:+rw\n')

        self._cmd('setfacl / -d u:user:a')

        self.terminal.reset_mock()
        self._cmd('getfacl /')
        with assert_mock(self.terminal) as t:
            t.write('user:user:+rw\n')
            t.write('user:user:-a\n')

        self._cmd('setfacl / -d u:user:w')

        self.terminal.reset_mock()
        self._cmd('getfacl /')
        with assert_mock(self.terminal) as t:
            t.write('user:user:+r\n')
            t.write('user:user:-aw\n')

        self.terminal.reset_mock()
        self._cmd('setfacl / -d u:user:G')
        with assert_mock(self.terminal) as t:
            t.write("No such permission 'G'\n")

    def test_tokenizer(self):
        arglist = r'set /computes/some\ file\ \ with\ spaces -v --help key=value other_key="quoted value" "lastkey"="escaped \" quotes"'

        eq_(self.oms_ssh.tokenizer.tokenize(arglist), [
            'set', '/computes/some file  with spaces', '-v', '--help', '=key',
            'value', '=other_key', 'quoted value', '=lastkey',
            'escaped " quotes'
        ])

        with assert_raises(CommandLineSyntaxError):
            self.oms_ssh.tokenizer.tokenize('ls " -l')

        arglist = r'set test cornercase="glued""quoted"'
        eq_(self.oms_ssh.tokenizer.tokenize(arglist),
            ['set', 'test', '=cornercase', 'gluedquoted'])

        arglist = r'set test # comment'
        eq_(self.oms_ssh.tokenizer.tokenize(arglist), ['set', 'test'])
class CmdCompletionTestCase(unittest.TestCase):

    def setUp(self):
        self.oms_ssh = OmsShellProtocol()
        auth = getUtility(IAuthentication, context=None)
        user = auth.getPrincipal('user')
        user.groups.append('admins')
        self.oms_ssh.batch = True
        self.oms_ssh.logged_in(user)
        self.oms_ssh.batch = False

        self.terminal = mock.Mock()
        self.oms_ssh.terminal = self.terminal

        self.oms_ssh.connectionMade()

        # the standard model doesn't have any command or path which
        # is a prefix of another (len > 1), I don't want to force changes
        # to the model just for testing completion, so we have monkey patch
        # the commands() function and add a command 'hello'.
        self.orig_commands = registry.commands

        class HelloCmd(Cmd):
            name = 'hello'
        registry.commands = lambda: dict(hello=HelloCmd, **self.orig_commands())

    def tearDown(self):
        registry.commands = self.orig_commands

    def make_compute(self, hostname=u'tux-for-test', state=u'active', arch=u'linux', memory=2000):
        return Compute(hostname, state, arch, memory)

    def _input(self, string):
        for s in string:
            self.oms_ssh.characterReceived(s, False)

    def _tab_after(self, string):
        self._input(string)
        self.terminal.reset_mock()

        self.oms_ssh.handle_TAB()

    def test_command_completion(self):
        self._tab_after('q')
        with assert_mock(self.terminal) as t:
            t.write('uit ')
            no_more_calls(t)

    def test_command_completion_spaces(self):
        self._tab_after('    q')
        with assert_mock(self.terminal) as t:
            t.write('uit ')
            no_more_calls(t)

    def test_complete_not_found(self):
        self._tab_after('asdasd')
        with assert_mock(self.terminal) as t:
            no_more_calls(t)

    def test_complete_quotes(self):
        self._tab_after('ls "comp')
        with assert_mock(self.terminal) as t:
            t.write('utes/')
            no_more_calls(t)

    def test_complete_prefix(self):
        self._tab_after('he')
        with assert_mock(self.terminal) as t:
            t.write('l')
            no_more_calls(t)

        # hit tab twice
        self.terminal.reset_mock()
        self.oms_ssh.handle_TAB()

        with assert_mock(self.terminal) as t:
            t.write('')
            t.nextLine()
            t.write('help  hello\n')
            t.write(self.oms_ssh.ps[0] + 'hel')
            no_more_calls(t)

    def test_spaces_between_arg(self):
        self._tab_after('ls comp')

        with assert_mock(self.terminal) as t:
            t.write('utes/')
            no_more_calls(t)

    def test_command_arg_spaces_before_command(self):
        self._tab_after(' ls comp')
        with assert_mock(self.terminal) as t:
            t.write('utes/')
            no_more_calls(t)

    def test_mandatory_positional(self):
        self._tab_after('cat ')
        with assert_mock(self.terminal) as t:
            skip(t, 4)
            no_more_calls(t)

    def test_complete_switches(self):
        self._tab_after('quit ')
        with assert_mock(self.terminal) as t:
            no_more_calls(t)

        # hit tab twice
        self.oms_ssh.handle_TAB()
        with assert_mock(self.terminal) as t:
            no_more_calls(t)

        # now try with a dash
        self._tab_after('-')
        with assert_mock(self.terminal) as t:
            t.write('')
            t.nextLine()
            t.write('-h  --help\n')
            t.write(self.oms_ssh.ps[0] + 'quit -')
            no_more_calls(t)
        # disambiguate
        self._tab_after('-')
        with assert_mock(self.terminal) as t:
            t.write('help ')
            no_more_calls(t)

    def test_complete_consumed_switches(self):
        self._tab_after('ls --help')
        with assert_mock(self.terminal) as t:
            t.write(' ')
            no_more_calls(t)

        self._tab_after('-')
        with assert_mock(self.terminal) as t:
            skip(t, 2)
            with current_call(t) as c:
                assert 'help' not in c.arg
                assert '-h' not in c.arg

    @run_in_reactor
    def test_complete_context_dependent_no_context(self):
        """Test whether context dependent arguments are correctly built when the
        context argument (i.e. the `set` cmd `path` argument) is not yet present.

        """
        self._tab_after('set /comp')
        with assert_mock(self.terminal) as t:
            t.write('utes/')
            no_more_calls(t)

    @run_in_reactor
    def test_complete_keyword_switches(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._tab_after('set /computes/%s st' % cid)
        with assert_mock(self.terminal) as t:
            t.write('ate=')
            no_more_calls(t)

        self._tab_after('ina')
        with assert_mock(self.terminal) as t:
            t.write('ctive ')
            no_more_calls(t)

    @run_in_reactor
    def test_complete_keyword_switches_mk(self):
        self.oms_ssh.lineReceived('cd computes')

        self._tab_after('mk compute st')
        with assert_mock(self.terminal) as t:
            t.write('ate=')
            no_more_calls(t)

        self._tab_after('ina')
        with assert_mock(self.terminal) as t:
            t.write('ctive ')
            no_more_calls(t)

    @run_in_reactor
    def test_complete_consumed_keyword_switches_mk(self):
        """Test consuming of already completed switches when there are mandatory arguments."""
        self.oms_ssh.lineReceived('cd computes')

        self._tab_after('mk compute st')
        with assert_mock(self.terminal) as t:
            t.write('ate=')
            no_more_calls(t)

        self._tab_after('ina')
        with assert_mock(self.terminal) as t:
            t.write('ctive ')
            no_more_calls(t)

        self._tab_after('st')
        assert not self.terminal.method_calls

    @run_in_reactor
    def test_complete_mk_legal_types(self):
        """Test that only legal types are shown."""
        self.oms_ssh.lineReceived('cd computes')

        self._tab_after('mk net')
        assert not self.terminal.method_calls

        self.oms_ssh.handle_RETURN()
        self.terminal.reset_mock()

        self._tab_after('mk comp')
        #~ eq_(self.terminal.method_calls, [('write', ('ute ',), {})])
        with assert_mock(self.terminal) as t:
            t.write('ute ')
            no_more_calls(t)

        self._tab_after('arch')
        with assert_mock(self.terminal) as t:
            t.write('itecture=')
            no_more_calls(t)

    @run_in_reactor
    def test_complete_mk_legal_types_interface(self):
        class ITest(Interface):
            pass

        class Test(Model):
            implements(ITest)

            def __init__(self):
                pass

        class TestInterfaceContainer(Container):
            __contains__ = ITest

        class TestClassContainer(Container):
            __contains__ = Test

        creatable_models['some-test'] = Test

        orig_current_object = commands.CreateObjCmd.current_obj

        try:
            commands.CreateObjCmd.current_obj = TestInterfaceContainer()
            self._tab_after('mk ')
            with assert_mock(self.terminal) as t:
                t.write('some-test ')
                no_more_calls(t)

            self.oms_ssh.handle_RETURN()
            self.terminal.reset_mock()

            commands.CreateObjCmd.current_obj = TestClassContainer()

            self._tab_after('mk ')
            with assert_mock(self.terminal) as t:
                t.write('some-test ')
                no_more_calls(t)
        finally:
            commands.CreateObjCmd.current_obj = orig_current_object
            del creatable_models['some-test']

    @run_in_reactor
    def test_complete_positional_choice(self):
        self.oms_ssh.lineReceived('cd computes')

        self._tab_after('mk comp')
        with assert_mock(self.terminal) as t:
            t.write('ute ')
            no_more_calls(t)

        self._tab_after('comp')
        assert not self.terminal.method_calls

    @run_in_reactor
    def test_complete_container_symlink(self):
        computes = db.get_root()['oms_root']['computes']
        cid = computes.add(self.make_compute())
        transaction.commit()

        self._tab_after('cd /computes/%s' % cid)
        with assert_mock(self.terminal) as t:
            t.write('/')
Beispiel #12
0
 def connection_made(self, terminal, size):
     self.shell = OmsShellProtocol()
     self.shell.set_terminal(terminal)
     self.shell.connectionMade()
     self.shell.terminalSize(size[0], size[1])
     self.shell.logged_in(self.principal)