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_)
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 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'])
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'])