def test_get_childnodes_order(self):
        """
        The inspector should return the childnodes in the order, as they were
        nested inside the node definition.
        """
        # Automatic
        class Root(Node):
            class A(Node): pass
            class B(Node): pass

        self.assertEqual(repr(Inspector(Root()).get_childnodes()), '[<Node Root.A>, <Node Root.B>]')
        self.assertEqual(repr(Inspector(Env(Root())).get_childnodes()), '[Env(Root.A), Env(Root.B)]')

        # Manually set creation order
        class Root(Node):
            class A(Node): pass
            class B(Node): pass
            A._node_creation_counter = 44
            B._node_creation_counter = 45

        self.assertEqual(repr(Inspector(Root()).get_childnodes()), '[<Node Root.A>, <Node Root.B>]')
        self.assertEqual(repr(Inspector(Env(Root())).get_childnodes()), '[Env(Root.A), Env(Root.B)]')

        # Manually revert the creation order
        class Root(Node):
            class A(Node): pass
            class B(Node): pass
            A._node_creation_counter = 45
            B._node_creation_counter = 44

        self.assertEqual(repr(Inspector(Root()).get_childnodes()), '[<Node Root.B>, <Node Root.A>]')
        self.assertEqual(repr(Inspector(Env(Root())).get_childnodes()), '[Env(Root.B), Env(Root.A)]')
    def test_auto_mapping_from_node_to_simplenode_array(self):
        # When no role_mapping is defined between Node and SimpleNode.Array,
        # we will map *all* roles to 'host'
        class A(Node):
            class Hosts:
                role1 = {LocalHost1, LocalHost2}
                role2 = {LocalHost3}
                role3 = {LocalHost4}

            class B(SimpleNode.Array):
                pass

        env = Env(A())
        self.assertEqual(len(list(env.B)), 4)

        # When we have a Hosts class, and no explicit
        # mapping between A and B, this hosts will be used.
        class A(Node):
            class Hosts:
                role1 = {LocalHost1, LocalHost2}
                role2 = {LocalHost3, LocalHost4}

            class B(SimpleNode.Array):
                class Hosts:
                    host = LocalHost1

        env = Env(A())
        self.assertEqual(len(list(env.B)), 1)

        # In case of JustOne, it should work as well.
        class A(Node):
            class Hosts:
                role1 = LocalHost1

            class B(SimpleNode.JustOne):
                pass

        env = Env(A())
        self.assertEqual(len(list(env.B)), 1)

        # Exception: Invalid initialisation of SimpleNode.JustOne. 2 hosts given to <Node A.B>.
        class A(Node):
            class Hosts:
                role1 = {LocalHost1, LocalHost2}

            class B(SimpleNode.JustOne):
                pass

        env = Env(A())
        self.assertRaises(Exception, lambda: env.B)
    def test_simple_node_getitem(self):
        class N(SimpleNode):
            class Hosts:
                host = {LocalHost1, LocalHost2}

            def func(self):
                return 'result'

        n = N()
        self.assertIsInstance(n[0], SimpleNode)
        self.assertIsInstance(n[1], SimpleNode)
        self.assertIsInstance(n[LocalHost1], SimpleNode)
        self.assertIsInstance(n[LocalHost2], SimpleNode)
        self.assertEqual(n[0]._node_is_isolated, True)
        self.assertEqual(n[1]._node_is_isolated, True)
        self.assertEqual(n[LocalHost1]._node_is_isolated, True)
        self.assertEqual(n[LocalHost2]._node_is_isolated, True)
        self.assertRaises(KeyError, lambda: n[2])
        self.assertRaises(KeyError, lambda: n[LocalHost3])

        # Calling the isolated item should not return an array
        env = Env(N())
        self.assertEqual(list(env.func()), ['result', 'result'])
        self.assertIsInstance(env.func(), ParallelActionResult)
        self.assertEqual(env[0].func(), 'result')
        self.assertEqual(env[1].func(), 'result')
        self.assertRaises(KeyError, lambda: env[2])
        self.assertRaises(KeyError, lambda: env[LocalHost3])
    def test_dont_isolate_yet(self):
        once = [0]
        for_each_host = [0]
        this = self

        class A(Node):
            class Hosts:
                my_role = {LocalHost1, LocalHost2}

            @map_roles('my_role')
            class B(SimpleNode.Array):
                def for_each_host(self):
                    for_each_host[0] += 1
                    this.assertEqual(len(self.hosts), 1)

                @dont_isolate_yet
                def only_once(self):
                    once[0] += 1
                    self.for_each_host()
                    this.assertEqual(len(self.hosts), 2)
                    return 'result'

        env = Env(A())
        result = env.B.only_once()

        self.assertEqual(result, 'result')
        self.assertEqual(once, [1])
        self.assertEqual(for_each_host, [2])
    def test_simple_nodes_in_normal_node(self):
        class N(Node):
            class Hosts:
                role1 = {LocalHost1, LocalHost}
                role2 = LocalHost3

            @map_roles('role1')
            class M(SimpleNode.Array):
                def func(self):
                    return 'func-m'

                class X(SimpleNode):
                    def func(self):
                        return 'func-x'

            def func(self):
                return 'func-n'

        # `M` should behave as an array.
        env = Env(N())
        self.assertEqual(env.func(), 'func-n')
        self.assertEqual(list(env.M.func()), ['func-m', 'func-m'])
        self.assertIsInstance(env.M.func(), ParallelActionResult)
        self.assertEqual(env.M[0].func(), 'func-m')
        self.assertEqual(list(env.M.X.func()), ['func-x', 'func-x'])
        self.assertIsInstance(env.M.X.func(), ParallelActionResult)
        self.assertEqual(env.M[0].X.func(), 'func-x')
        self.assertEqual(env.M.X[0].func(), 'func-x')
    def test_additional_roles_in_simple_node(self):
        # We should be able to pass additional roles to a SimpleNode, but
        # isolation happens at the 'host' role.
        this = self
        counter = [0]

        class A(Node):
            class Hosts:
                role1 = {LocalHost1, LocalHost2, LocalHost3}
                role2 = {LocalHost2, LocalHost4, LocalHost5}

            @map_roles('role1', extra='role2')
            class B(SimpleNode.Array):
                def action(self):
                    this.assertEqual(len(self.hosts.filter('host')), 1)
                    this.assertEqual(len(self.hosts.filter('extra')), 3)
                    this.assertEqual(set(self.hosts.roles),
                                     set(['host', 'extra']))
                    self.C.action2()
                    counter[0] += 1

                class C(SimpleNode):
                    def action2(self):
                        this.assertEqual(len(self.hosts.filter('host')), 1)
                        this.assertEqual(len(self.hosts.filter('extra')), 3)
                        this.assertEqual(set(self.hosts.roles),
                                         set(['host', 'extra']))
                        counter[0] += 1

        env = Env(A())
        env.B.action()
        self.assertEqual(counter[0], 6)
    def test_env_object(self):
        class S(Node):
            class Hosts:
                role1 = LocalHost
                role2 = LocalHost2

            def my_action(self):
                return 'result'

            def return_name_of_self(self):
                return self.__class__.__name__

            def echo_on_all(self):
                return self.hosts.run('/bin/echo echo', interactive=False)

            def echo_on_role1(self):
                return self.hosts.filter('role1').run('/bin/echo echo',
                                                      interactive=False)

            def echo_on_role2(self):
                return self.hosts.filter('role2')[0].run('/bin/echo echo',
                                                         interactive=False)

        s = S()
        env = Env(s)

        self.assertEqual(env.my_action(), 'result')
        self.assertEqual(env.return_name_of_self(), 'Env')
        self.assertEqual(env.echo_on_all(), ['echo\r\n', 'echo\r\n'])
        self.assertEqual(env.echo_on_role1(), ['echo\r\n'])
        self.assertEqual(env.echo_on_role2(), 'echo\r\n')

        # Isinstance hooks
        self.assertIsInstance(s, S)
        self.assertIsInstance(env, S)
    def test_simple_node(self):
        class N(SimpleNode):
            class Hosts:
                host = {LocalHost1, LocalHost2}

            def func(self):
                return 'result'

        # SimpleNode executes for each host separately.
        env = Env(N())
        self.assertEqual(list(env.func()), ['result', 'result'])

        # Test return value of SimpleNode
        result = env.func()
        self.assertIsInstance(result, ParallelActionResult)

        # (The key is the Env, containing the isolation)
        self.assertIsInstance(result.keys()[0], Env)
        self.assertIsInstance(result.keys()[1], Env)
        self.assertIn(result.keys()[0]._node.Hosts.host,
                      {LocalHost1, LocalHost2})
        self.assertIn(result.keys()[1]._node.Hosts.host,
                      {LocalHost1, LocalHost2})

        self.assertEqual(result.values()[0], 'result')
        self.assertEqual(result.values()[1], 'result')
 def setUp(self):
     class Root(Node):
         attr = 'value'
         query = Q.attr + Q.attr
         def my_action(self): pass
     self.env = Env(Root())
     self.env_insp = Inspector(self.env)
     self.node_insp = Inspector(Root())
    def test_q_navigation(self):
        this = self

        class DummyException(Exception):
            pass

        class MyNode(Node):
            class Hosts:
                host = LocalHost

            # 1. Normal query from node.
            attr = 'value'
            query = Q.attr
            query2 = Q.attr + Q.attr

            def my_action(self):
                return self.query

            # 2. Exception in query from node.

            @property
            def this_raises(self):
                raise DummyException

            query3 = Q.this_raises + Q.attr

            def my_action2(self):
                # Calling query3 raises a QueryException
                # The inner exception that one is the DummyException
                try:
                    return self.query3
                except Exception as e:
                    this.assertIsInstance(e, ActionException)

                    this.assertIsInstance(e.inner_exception, QueryException)
                    this.assertIsInstance(e.inner_exception.node, MyNode)
                    this.assertIsInstance(e.inner_exception.query, Query)

                    this.assertIsInstance(e.inner_exception.inner_exception,
                                          DummyException)

                    # Raising the exception again at this point, will turn it
                    # into an action exception.
                    raise

        s = MyNode()
        env = Env(s)

        # 1.
        self.assertEqual(env.my_action(), 'value')
        self.assertEqual(env.query2, 'valuevalue')

        # 2.
        self.assertRaises(ActionException, env.my_action2)
        try:
            env.my_action2()
        except Exception as e:
            self.assertIsInstance(e, ActionException)
Example #11
0
 def run_action(self, action_name, *a, **kw):
     """
     Run a deployment command at the current shell state.
     """
     env = Env(self.state._node,
               self.pty,
               self.logger_interface,
               is_sandbox=False)
     return getattr(env, action_name)(*a, **kw)
    def test_nested_action(self):
        class MyNode(Node):
            class Node2(Node):
                class Node3(Node):
                    def my_action(self):
                        return 'result'

        env = Env(MyNode())
        result = env.Node2.Node3.my_action()
        self.assertEqual(result, 'result')
Example #13
0
 def _get_env(self):
     """
     Created a sandboxed environment for evaluation of attributes.
     (attributes shouldn't have side effects on servers, so sandboxing is fine.)
     Returns an ``Env`` object.
     """
     env = Env(self.node,
               self.shell.pty,
               self.shell.logger_interface,
               is_sandbox=True)
     return Console(self.shell.pty).select_node_isolation(env)
    def test_node_console(self):
        class Root(Node):
            def a(self):
                return 'result'

        # Test Console instance.
        p = Pty()
        env = Env(Root(), pty=p)
        self.assertIsInstance(env.console, Console)
        self.assertEqual(env.console.pty, p)
        self.assertEqual(env.console.is_interactive, p.interactive)  #
    def test_nested_simple_nodes(self):
        class N(SimpleNode):
            class Hosts:
                host = {LocalHost1, LocalHost2}

            class M(SimpleNode):
                def func(self):
                    return 'result'

        # `M` gets both hosts as well.
        env = Env(N())
        self.assertEqual(list(env.M.func()), ['result', 'result'])
    def test_super_call(self):
        # Calling the superclass
        class A(Node):
            def action(self):
                return 'result'

        class B(A):
            def action(self):
                return 'The result was %s' % A.action(self)

        env = Env(B())
        self.assertEqual(env.action(), 'The result was result')
    def test_wrapping_middle_node_in_env(self):
        class S(Node):
            class Hosts:
                role1 = {LocalHost1, LocalHost2}

            class T(Node):
                def action(self):
                    return len(self.hosts)

        s = S()
        env = Env(s.T)
        self.assertEqual(env.action(), 2)
    def setUp(self):
        class Root(Node):
            def a(self):
                pass

            @suppress_action_result
            def b(self):
                pass

        self.env = Env(Root())
        self.env_insp = Inspector(self.env)
        self.node_insp = Inspector(Root())
    def test_action_with_params(self):
        class MyNode(Node):
            class Hosts:
                host = LocalHost

            def my_action(self, param1, *a, **kw):
                return (param1, a, kw)

        s = MyNode()
        env = Env(s)
        result = env.my_action('param1', 1, 2, k=3, v=4)
        self.assertEqual(result, ('param1', (1, 2), {'k': 3, 'v': 4}))
    def test_action_aliases(self):
        # We can define multiple aliases for an action.
        class A(Node):
            @alias('my_alias2')
            @alias('my_alias')
            def action(self):
                return 'result'

        env = Env(A())
        self.assertEqual(env.action(), 'result')
        self.assertEqual(env.my_alias(), 'result')
        self.assertEqual(env.my_alias2(), 'result')
    def test_node_names(self):
        class Another(Node):
            pass

        class N(Node):
            class Hosts:
                role1 = {LocalHost1, LocalHost2}
                role2 = LocalHost3

            class M(Node):
                class O(Node):
                    pass

            @map_roles(host='role1')
            class P(SimpleNode.Array):
                pass

            class another_node(Another):
                pass

            another_node2 = Another

        # For the class definitions, the names certainly shoudn't change.
        self.assertEqual(N.__name__, 'N')
        self.assertEqual(N.M.__name__, 'M')
        self.assertEqual(N.M.O.__name__, 'O')
        self.assertEqual(N.P.__name__, 'P')
        self.assertEqual(N.another_node.__name__, 'another_node')
        self.assertEqual(N.another_node2.__name__, 'Another')

        # For instances (and mappings), they should be named according to the
        # full path.
        n = N()
        self.assertEqual(repr(n), '<Node N>')
        self.assertEqual(repr(n.M), '<Node N.M>')
        self.assertEqual(repr(n.M.O), '<Node N.M.O>')
        self.assertEqual(repr(n.P), '<Node N.P>')

        self.assertEqual(repr(n.P[0]), '<Node N.P[0]>')
        self.assertEqual(repr(n.P[1]), '<Node N.P[1]>')

        self.assertEqual(repr(n.another_node), '<Node N.another_node>')
        self.assertEqual(repr(n.another_node2), '<Node N.another_node2>')

        # Test Env.__repr__
        env = Env(n)
        self.assertEqual(repr(env), 'Env(N)')
        self.assertEqual(repr(env.M.O), 'Env(N.M.O)')
        self.assertEqual(repr(env.P[0]), 'Env(N.P[0])')
        self.assertEqual(repr(env.P[1]), 'Env(N.P[1])')
    def test_assignments_in_node(self):
        # It's not allowed to change attributes from a Node Environment.
        class MyNode(Node):
            def action(self):
                self.variable = 'value'

        # AttributeError wrapped in ActionException
        env = Env(MyNode())
        self.assertRaises(ActionException, env.action)

        try:
            env.action()
        except ActionException as e:
            self.assertIsInstance(e.inner_exception, AttributeError)
    def test_action_alias(self):
        """
        By using the @alias decorator around an action, the action should be
        available through multiple names.
        """
        class Root(Node):
            @alias('b.c.d')
            @alias('b')
            def a(self):
                return 'result'

        env = Env(Root())
        self.assertEqual(env.a(), 'result')
        self.assertEqual(env.b(), 'result')
        self.assertEqual(getattr(env, 'b.c.d')(), 'result')
    def test_property(self):
        class MyNode(Node):
            class Hosts:
                host = LocalHost

            @property
            def p(self):
                return 'value'

            def my_action(self):
                return self.p

        s = MyNode()
        env = Env(s)
        self.assertEqual(env.my_action(), 'value')
    def test_required_property(self):
        class A(Node):
            p = required_property()

            def action(self):
                self.p()

        env = Env(A())

        # NotImplementedError wrapped in ActionException
        self.assertRaises(ActionException, env.action)
        try:
            env.action()
        except ActionException as e:
            self.assertIsInstance(e.inner_exception, NotImplementedError)
Example #26
0
    def __call__(self):
        from deployer.contrib.nodes import connect

        class Connect(connect.Connect):
            class Hosts:
                host = self.node.hosts.get_hosts()

        env = Env(Connect(), self.shell.pty, self.shell.logger_interface)

        # Run as any other action. (Nice exception handling, e.g. in case of NoInput on host selection.)
        try:
            env.with_host()
        except ActionException as e:
            pass
        except Exception as e:
            self.shell.logger_interface.log_exception(e)
    def test_action_names(self):
        # Test Action.__repr__
        class N(Node):
            class M(Node):
                def my_action(self):
                    pass

        n = N()
        self.assertEqual(repr(n.M.my_action), '<Action N.M.my_action>')
        self.assertEqual(repr(N.M.my_action), '<Unbound Action my_action>')
        self.assertEqual(n.M.my_action.name, 'my_action')

        # Env.Action.__repr__
        env = Env(n)
        self.assertEqual(repr(env.M.my_action), '<Env.Action N.M.my_action>')
        self.assertEqual(env.M.my_action.name, 'my_action')
    def test_bin_false(self):
        class S(Node):
            class Hosts:
                role1 = LocalHost

            def return_false(self):
                return self.hosts.run('/bin/false', interactive=False)

        s = S()
        env = Env(s)

        # This should raise an ExecCommandFailed, wrapped in an ActionException
        self.assertRaises(ActionException, env.return_false)

        try:
            env.return_false()
        except ActionException as e:
            self.assertIsInstance(e.inner_exception, ExecCommandFailed)
    def test_simplenode_just_one(self):
        # In contrast with .Array, the .JustOne should
        # make sure that it doesn't behave as an array,
        # but instead asserts that it will only get one
        # host for this role.
        class A(Node):
            class Hosts:
                role1 = {LocalHost1, LocalHost2}
                role2 = LocalHost3

            @map_roles('role2')
            class B(SimpleNode.JustOne):
                def action(self):
                    return 'result'

        env = Env(A())
        self.assertEqual(env.B._node_is_isolated, True)
        self.assertEqual(env.B.action(), 'result')
Example #30
0
    def __call__(self):
        # Execute
        logger_interface = self.shell.logger_interface

        try:
            # Create env
            env = Env(self.node,
                      self.shell.pty,
                      logger_interface,
                      is_sandbox=self.sandbox)

            # Call action
            if self.attr_name is not None:
                result = getattr(env, self.attr_name)(*self.args)
                suppress_result = Inspector(
                    self.node).suppress_result_for_action(self.attr_name)
            else:
                result = env(*self.args)
                suppress_result = False

            # When the result is a subnode, start a subshell.
            def handle_result(result):
                if isinstance(result, deployer.node.Env):
                    print ''
                    print 'Starting subshell ...'
                    self.shell.state = ShellState(
                        result._node, return_state=self.shell.state)

                # Otherwise, print result
                elif result is not None and not suppress_result:
                    print result

            if isinstance(result, list):
                for r in result:
                    handle_result(r)
            else:
                handle_result(result)

        except ActionException as e:
            # Already sent to logger_interface in the Action itself.
            pass

        except Exception as e:
            logger_interface.log_exception(e)