Exemple #1
0
 def fill_database(self):
     self.o = Orchestration(name='name', version=1, description='desc')
     self.at = ActionTemplate(name='action_name',
                              version=1,
                              action_type=ActionType.SHELL,
                              code='')
     db.session.add_all([self.o, self.at])
    def fill_database(self):
        v1 = Vault(user_id=ROOT, name='home', value='home_content')
        v2 = Vault(user_id=ROOT, name='command', value='command_content')
        v3 = Vault(user_id=ROOT, name='foo', value='foo_content')

        at = ActionTemplate('vault var in mapping',
                            1,
                            action_type=ActionType.TEST,
                            code='{{input.command}}',
                            schema=dict(
                                input=dict(command=dict(type='string')),
                                required=['command']),
                            id=self.ACTION_TEMPLATE1)
        at2 = ActionTemplate('vault var in action template code',
                             1,
                             action_type=ActionType.TEST,
                             code='{{vault.foo}}',
                             schema=dict(required=['vault.foo']),
                             id=self.ACTION_TEMPLATE2)
        o = Orchestration('vault', 1, id=self.ORCH)
        s1 = o.add_step(False,
                        name='vault var in step code',
                        action_type=ActionType.TEST,
                        code="{{vault.home}}")
        s2 = o.add_step(
            False,
            action_template=at,
            schema=dict(mapping={'command': {
                'from': 'vault.command'
            }}),
            parents=[s1])
        s3 = o.add_step(False, action_template=at2, parents=[s2])

        db.session.add_all([v1, v2, v3, o])
Exemple #3
0
    def test_init_on_load(self):
        o = Orchestration(id='aaaaaaaa-1234-5678-1234-aaaaaaaa0001',
                          name='Test Orchestration',
                          version=1,
                          description='description')

        s1 = o.add_step(undo=False,
                        action_template=self.at,
                        parents=[],
                        children=[],
                        stop_on_error=False,
                        id='bbbbbbbb-1234-5678-1234-bbbbbbbb0001')

        db.session.add(o)
        db.session.commit()
        del o

        o = Orchestration.query.get('aaaaaaaa-1234-5678-1234-aaaaaaaa0001')

        self.assertEqual({s1: []}, o._graph.succ)

        s2 = o.add_step(undo=False,
                        action_template=self.at,
                        parents=[],
                        children=[],
                        stop_on_error=False,
                        id='bbbbbbbb-1234-5678-1234-bbbbbbbb0002')
        del o

        o = Orchestration.query.get('aaaaaaaa-1234-5678-1234-aaaaaaaa0001')

        self.assertEqual({s1: [], s2: []}, o._graph.succ)
    def test_validate_input_chain_mapping_action_template(self):
        o = Orchestration("test", 1)
        at = ActionTemplate("action",
                            1,
                            action_type=ActionType.SHELL,
                            code='',
                            schema={
                                'input': {
                                    'param1': {}
                                },
                                'required': ['param1']
                            })

        s1 = o.add_step(undo=False,
                        action_template=at,
                        schema={'mapping': {
                            'param1': "value"
                        }})
        s2 = o.add_step(undo=False,
                        action_type=ActionType.SHELL,
                        schema={
                            'input': {
                                'param2': {}
                            },
                            'required': ['param2']
                        })

        validate_input_chain(o, params=dict(input={'param2'}))

        with self.assertRaises(errors.MissingParameters) as e:
            validate_input_chain(o, params=dict(input={'param1'}))

        self.assertEqual(['input.param2'], e.exception.parameters)
    def fill_database(self):
        soft = Software(name="python",
                        version="3.8.3",
                        family="programming",
                        filename=self.filename,
                        size=self.size,
                        checksum=self.checksum,
                        id=self.SOFTWARE,
                        last_modified_at=defaults.INITIAL_DATEMARK.strftime(
                            defaults.DATEMARK_FORMAT))

        ssa = SoftwareServerAssociation(
            software_id=self.SOFTWARE,
            server_id=self.SERVER1,
            path=self.source_folder,
            last_modified_at=defaults.INITIAL_DATEMARK.strftime(
                defaults.DATEMARK_FORMAT))

        o = Orchestration(name="install python 3.8.3",
                          version=1,
                          description="installs python 3.8.3",
                          id=self.ORCH,
                          last_modified_at=defaults.INITIAL_DATEMARK.strftime(
                              defaults.DATEMARK_FORMAT))

        s1 = o.add_step(id=self.STEP1,
                        action_template=ActionTemplate.query.get(
                            ActionTemplate.SEND_SOFTWARE),
                        undo=False,
                        undo_on_error=False,
                        schema={
                            "mapping": {
                                "software": self.SOFTWARE,
                                "server": {
                                    "from": "env.server_id"
                                }
                            }
                        },
                        last_modified_at=defaults.INITIAL_DATEMARK.strftime(
                            defaults.DATEMARK_FORMAT))
        o.add_step(id=self.STEP2,
                   undo=False,
                   undo_on_error=False,
                   parents=[s1],
                   code="{{input.file}}",
                   action_type="SHELL",
                   expected_rc=0,
                   last_modified_at=defaults.INITIAL_DATEMARK.strftime(
                       defaults.DATEMARK_FORMAT))
        db.session.add_all([soft, ssa, o])
 def fill_database(self):
     self.at = ActionTemplate(name='action_name',
                              version=1,
                              action_type=ActionType.SHELL,
                              code='')
     self.o = Orchestration(name='name', version=1)
     self.st1 = self.o.add_step(undo=False, action_template=self.at)
     self.st2 = self.o.add_step(undo=False,
                                action_template=self.at,
                                children=[self.st1])
     self.st3 = self.o.add_step(undo=False,
                                action_template=self.at,
                                children=[self.st2])
     db.session.add_all([self.at, self.o])
    def test_validate_input_chain(self):
        o = Orchestration("test", 1)
        s1 = o.add_step(undo=False,
                        schema={
                            'input': {
                                'param1': {}
                            },
                            'required': ['param1'],
                            'output': {
                                'param2': {}
                            }
                        },
                        action_type=ActionType.SHELL,
                        code='')
        s2 = o.add_step(undo=False,
                        parents=[s1],
                        schema={
                            'input': {
                                'param3': {},
                                'param4': {}
                            },
                            'required': ['param3', 'param4'],
                            'mapping': {
                                'param3': {
                                    'from': 'param2'
                                }
                            }
                        },
                        action_type=ActionType.SHELL,
                        code='')
        s3 = o.add_step(undo=False,
                        parents=[s2],
                        schema={
                            'input': {
                                'param2': {},
                                'param5': {}
                            },
                            'required': ['param2']
                        },
                        action_type=ActionType.SHELL,
                        code='')

        validate_input_chain(o, params=dict(input={'param1', 'param4'}))

        with self.assertRaises(errors.MissingParameters) as e:
            validate_input_chain(o, params=dict(input={'param1'}))

        self.assertEqual(['input.param4'], e.exception.parameters)
Exemple #8
0
    def test_orchestration_resource_list(self):
        resp = self.client.get(url_for('api_1_0.orchestrationlist'),
                               headers=self.auth.header)

        self.assertEqual([], resp.get_json())

        o = Orchestration(name='orchestration_name',
                          version=1,
                          description='desc')

        db.session.add(o)
        db.session.commit()

        resp = self.client.get(url_for('api_1_0.orchestrationlist'),
                               headers=self.auth.header)

        self.assertEqual([o.to_json(add_params=True)], resp.get_json())
    def test_create_cmd_from_orchestration_one_step(self):
        at = ActionTemplate(id='aaaaaaaa-1234-5678-1234-aaaaaaaa0001',
                            name='create dir',
                            version=1,
                            action_type=ActionType.SHELL,
                            code='mkdir {{dir}}',
                            expected_stdout='',
                            expected_rc=0,
                            system_kwargs={})

        o = Orchestration('Test Orchestration',
                          1,
                          'description',
                          id='bbbbbbbb-1234-5678-1234-bbbbbbbb0001')

        me = Server('me',
                    port=5000,
                    me=True,
                    id='cccccccc-1234-5678-1234-cccccccc0001')
        remote = Server('remote',
                        port=5000,
                        id='cccccccc-1234-5678-1234-cccccccc0002')

        db.session.add_all([me, remote, o])

        s1 = o.add_step(id='eeeeeeee-1234-5678-1234-eeeeeeee0001',
                        undo=False,
                        action_template=at,
                        parents=[],
                        target=[])

        cc = create_cmd_from_orchestration(o,
                                           Context({'dir': './test_folder'}),
                                           hosts={'all': [me.id]},
                                           executor=None,
                                           register=mock.Mock())

        c1, = cc._dag.get_nodes_at_level(1)
        self.assertTupleEqual(('cccccccc-1234-5678-1234-cccccccc0001',
                               'eeeeeeee-1234-5678-1234-eeeeeeee0001'), c1.id)
        self.assertIsInstance(c1, Command)
        self.assertTrue(c1.stop_on_error)
        self.assertTrue(c1.undo_on_error)
        self.assertIsNone(c1.stop_undo_on_error)
Exemple #10
0
    def test_orch_creation(self):
        o = Orchestration(
            id="aaaaaaaa-1234-5678-1234-56781234aaa1",
            name='Test Orchestration',
            version=1,
            description='description',
        )

        db.session.add(o)
        db.session.commit()
        del o
        o = Orchestration.query.get("aaaaaaaa-1234-5678-1234-56781234aaa1")
    def test_validate_input_chain_vault_container(self):
        o = Orchestration("test", 1)
        at1 = ActionTemplate("action1",
                             1,
                             action_type=ActionType.SHELL,
                             code='',
                             schema={
                                 'input': {
                                     'vault.var': {}
                                 },
                                 'required': ['vault.var']
                             })

        s1 = o.add_step(undo=False, action_template=at1, schema={})

        validate_input_chain(o, params=dict(vault={'var'}))

        with self.assertRaises(errors.MissingParameters) as e:
            validate_input_chain(o, params=dict())

        self.assertEqual(['vault.var'], e.exception.parameters)
    def test_validate_input_chain_mapping_default_value(self):
        o = Orchestration("test", 1)
        at1 = ActionTemplate("action1",
                             1,
                             action_type=ActionType.SHELL,
                             code='',
                             schema={
                                 'input': {
                                     'input1': {},
                                     'input2': {
                                         'default': 1
                                     }
                                 },
                                 'required': ['input1'],
                                 'output': ['out1', 'out2']
                             })
        at2 = ActionTemplate("action2",
                             1,
                             action_type=ActionType.SHELL,
                             code='',
                             schema={
                                 'input': {
                                     'out1': {},
                                     'out2': {},
                                     'input2': {}
                                 },
                                 'required': ['out1', 'out2', 'input2'],
                                 'output': []
                             })

        s1 = o.add_step(undo=False, action_template=at1, schema={})
        s2 = o.add_step(undo=False,
                        action_template=at2,
                        schema={},
                        parents=[s1])

        with self.assertRaises(errors.MissingParameters) as e:
            validate_input_chain(o, params=dict(input={'input1'}))

        self.assertEqual(['input.input2'], e.exception.parameters)
    def fill_database(self):
        # create data for orchestration
        at = ActionTemplate(name='mkdir',
                            version=1,
                            action_type=ActionType.SHELL,
                            code='mkdir -f {{input.folder}}',
                            schema={
                                'input': {
                                    'folder': {
                                        'type': 'string'
                                    }
                                },
                                'required': ['folder']
                            },
                            expected_rc=0)
        o = Orchestration(name='Create folder',
                          version=1,
                          description="creates a folder")
        s = o.add_step(undo=False, action_template=at)

        db.session.add_all([at, o, s])
        o_wrapper = Orchestration(name="launch create folder", version=1)

        s21 = o_wrapper.add_step(
            undo=False,
            action_template=ActionTemplate.query.get(
                ActionTemplate.ORCHESTRATION),
            schema={'mapping': {
                'orchestration': str(o.id),
            }},
            parents=[])

        db.session.add_all([o_wrapper, s21])
Exemple #14
0
    def test_parameters(self):
        o = Orchestration(id=1,
                          name='Test Orchestration',
                          version=1,
                          description='description')

        s1 = o.add_step(undo=False,
                        action_template=self.at,
                        parents=[],
                        children=[],
                        stop_on_error=False,
                        id=1)

        at = ActionTemplate(name='action',
                            version=1,
                            action_type=ActionType.SHELL,
                            code='code to run',
                            expected_stdout='expected output',
                            expected_rc=0,
                            system_kwargs={})

        s1.action_template = at
    def fill_database(self):
        self.o = Orchestration('create user', version=1)
        self.at1 = ActionTemplate(action_type=ActionType.SHELL, name="create folder", version=1,
                                  code="sudo mkdir -p {{folder}}")
        self.at2 = ActionTemplate(action_type=ActionType.SHELL, name="delete folder", version=1,
                                  code="sudo rm -fr {{folder}}")
        self.at3 = ActionTemplate(action_type=ActionType.SHELL, name="create user", version=1,
                                  code="sudo useradd -d {{home}} {{user}}")
        self.at4 = ActionTemplate(action_type=ActionType.SHELL, name="delete user", version=1,
                                  code="sudo userdel {{user}}")

        self.st1 = self.o.add_step(undo=False, action_template=self.at1)
        self.st2 = self.o.add_step(undo=True, action_template=self.at2, parents=[self.st1])
        self.st3 = self.o.add_step(undo=False, action_template=self.at3, parents=[self.st1])
        self.st4 = self.o.add_step(undo=True, action_template=self.at4, parents=[self.st3])
        db.session.add(self.o)

        self.o2 = Orchestration('create user', version=2)
        self.st21 = self.o2.add_step(undo=False, action_template=self.at1)
        db.session.add(self.o2)

        db.session.commit()
 def post(self):
     json_data = request.get_json()
     generated_version = False
     if 'version' not in json_data:
         generated_version = True
         json_data['version'] = Orchestration.query.filter_by(
             name=json_data['name']).count() + 1
     o = Orchestration(**json_data)
     db.session.add(o)
     db.session.commit()
     resp_data = {'id': str(o.id)}
     if generated_version:
         resp_data.update(version=o.version)
     return resp_data, 201
Exemple #17
0
    def test_set_dependencies(self):
        s1 = Step(orchestration=None,
                  undo=False,
                  stop_on_error=False,
                  action_template=self.at,
                  id='11111111-2222-3333-4444-555555550001')
        s2 = Step(orchestration=None,
                  undo=False,
                  stop_on_error=False,
                  action_template=self.at,
                  id='11111111-2222-3333-4444-555555550002')
        s3 = Step(orchestration=None,
                  undo=False,
                  stop_on_error=False,
                  action_template=self.at,
                  id='11111111-2222-3333-4444-555555550003')

        o = Orchestration('test',
                          1,
                          id='11111111-2222-3333-4444-555555550004',
                          description='description test',
                          steps=[s1, s2, s3],
                          dependencies={
                              s1.id: [s2.id],
                              s2.id: [s3.id],
                              s3.id: []
                          })

        self.assertDictEqual(o.dependencies, {s1: [s2], s2: [s3], s3: []})

        self.assertListEqual(o.steps, [s1, s2, s3])

        with self.assertRaises(ValueError):
            o.set_dependencies({s1: [s2], s2: [s3], s3: [4]})

        o = Orchestration('test',
                          1,
                          id='11111111-2222-3333-4444-555555550004',
                          description='description test',
                          steps=[s1, s2, s3])

        self.assertListEqual([s1, s2, s3], o.steps)
        o.set_dependencies([(s1, s2), (s2, s3)])
        self.assertDictEqual(o.dependencies, {s1: [s2], s2: [s3], s3: []})
Exemple #18
0
    def setUp(self):
        """Create and configure a new app instance for each test."""
        # create the app with common test config
        self.app = create_app('test')
        self.app_context = self.app.app_context()
        self.app_context.push()
        self.client = self.app.test_client()
        self.headers = {"Authorization": f"Bearer {create_access_token('00000000-0000-0000-0000-000000000001')}"}

        db.create_all()

        self.at1 = ActionTemplate(id='11111111-2222-3333-4444-555555550001', name='action1', version=1,
                                  action_type=ActionType.SHELL, code='code to run',
                                  expected_stdout='expected output', expected_stderr='stderr', expected_rc=0,
                                  system_kwargs={})

        self.at2 = ActionTemplate(id='11111111-2222-3333-4444-555555550002', name='action2', version=1,
                                  action_type=ActionType.SHELL, code='code to run',
                                  expected_stdout='expected output', expected_stderr='stderr', expected_rc=0,
                                  system_kwargs={})

        self.o = Orchestration(id='11111111-2222-3333-4444-666666660001', name='orchestration_name',
                               version=1, description='desc')
    def test_validate_input_chain_mapping_with_orch(self):
        o = Orchestration('Schema Orch',
                          1,
                          id='00000000-0000-0000-0000-000000000001')
        s1 = o.add_step(id=1,
                        action_type=ActionType.SHELL,
                        undo=False,
                        schema={
                            'input': {
                                '1_a': {},
                                '1_b': {}
                            },
                            'required': ['1_b'],
                            'output': ['1_c']
                        })
        s2 = o.add_step(undo=False,
                        action_type=ActionType.SHELL,
                        schema={
                            'input': {
                                '2_a': {}
                            },
                            'required': ['2_a'],
                            'output': ['2_b']
                        })

        s3 = o.add_step(undo=False,
                        action_type=ActionType.SHELL,
                        parents=[s1],
                        schema={
                            'input': {
                                '3_a': {},
                                '3_b': {}
                            },
                            'required': ['3_a'],
                            'mapping': {
                                '3_a': {
                                    'from': '2_b'
                                }
                            }
                        })

        db.session.add(o)
        o2 = Orchestration('Schema Orch', 1)
        at = ActionTemplate.query.filter_by(name='orchestration',
                                            version=1).one()
        s1 = o2.add_step(id=1,
                         action_template=at,
                         undo=False,
                         schema={'mapping': {
                             'orchestration': o.id
                         }})
        s2 = o2.add_step(undo=False,
                         action_type=1,
                         parents=[s1],
                         schema={
                             'input': {
                                 "1": {},
                                 "2": {}
                             },
                             'required': ["1"],
                             'mapping': {
                                 "1": {
                                     'from': '1_c'
                                 }
                             }
                         })

        validate_input_chain(o2, params=dict(input={'hosts', '1_b', '2_a'}))
Exemple #20
0
    def test_attributes_and_methods(self):
        o = Orchestration(id=1,
                          name='Test Orchestration',
                          version=1,
                          description='description')

        s1 = o.add_step(undo=False,
                        action_template=self.at,
                        parents=[],
                        children=[],
                        stop_on_error=False,
                        id=1)
        s2 = o.add_step(undo=True,
                        action_template=self.at,
                        parents=[s1],
                        children=[],
                        stop_on_error=False,
                        id=2)

        with self.assertRaises(errors.ParentUndoError):
            o.add_step(undo=False,
                       action_template=self.at,
                       parents=[s2],
                       children=[],
                       stop_on_error=False)
        self.assertListEqual(o.steps, [s1, s2])

        o2 = Orchestration('dto', 2, id=2)
        s21 = o2.add_step(undo=False,
                          action_template=self.at,
                          parents=[],
                          children=[],
                          stop_on_error=False,
                          id=21)

        with self.assertRaises(ValueError):
            o.add_parents(s21, [s1])

        o.delete_step(s2)

        self.assertListEqual(o.steps, [s1])
        self.assertListEqual(o.children[s1], [])

        s2 = o.add_step(undo=False,
                        action_template=self.at,
                        parents=[s1],
                        children=[],
                        stop_on_error=False,
                        id=2)
        s3 = o.add_step(undo=False,
                        action_template=self.at,
                        parents=[s2],
                        children=[],
                        stop_on_error=False,
                        id=3)
        s4 = o.add_step(undo=False,
                        action_template=self.at,
                        parents=[s2],
                        children=[],
                        stop_on_error=False,
                        id=4)
        s5 = o.add_step(undo=False,
                        action_template=self.at,
                        parents=[s4],
                        children=[],
                        stop_on_error=False,
                        id=5)
        s6 = o.add_step(undo=False,
                        action_template=self.at,
                        parents=[s4],
                        children=[],
                        stop_on_error=False,
                        id=6)
        s7 = o.add_step(undo=False,
                        action_template=self.at,
                        parents=[s1],
                        children=[s2],
                        stop_on_error=False,
                        id=7)

        self.assertListEqual(o.steps, [s1, s2, s3, s4, s5, s6, s7])

        self.assertDictEqual(
            o.children, {
                s1: [s2, s7],
                s2: [s3, s4],
                s3: [],
                s4: [s5, s6],
                s5: [],
                s6: [],
                s7: [s2]
            })
        self.assertDictEqual(o.parents, {
            s1: [],
            s2: [s1, s7],
            s3: [s2],
            s4: [s2],
            s5: [s4],
            s6: [s4],
            s7: [s1]
        })

        with self.assertRaises(errors.ChildDoError):
            o.add_step(undo=True,
                       action_template=self.at,
                       parents=[s1],
                       children=[s2],
                       stop_on_error=False)

        with self.assertRaises(errors.CycleError):
            o.add_step(undo=False,
                       action_template=self.at,
                       parents=[s6],
                       children=[s1],
                       stop_on_error=False)

        # Check parent functions
        o.add_parents(s6, [s2])
        self.assertListEqual(o.children[s2], [s3, s4, s6])

        o.set_parents(s6, [s3, s4])
        self.assertListEqual(o.parents[s6], [s3, s4])

        o.delete_parents(s6, [s3, s2])
        self.assertListEqual(o.parents[s6], [s4])

        # Check children functions
        o.add_children(s3, [s6])
        self.assertListEqual(o.parents[s6], [s4, s3])

        o.delete_children(s4, [s5, s6])
        self.assertListEqual(o.parents[s6], [s3])
        self.assertListEqual(o.parents[s5], [])

        o.set_children(s4, [s5, s6]).set_children(s3, [])
        self.assertListEqual([s4], o.parents[s6])
        self.assertListEqual([s4], o.parents[s5])
        self.assertListEqual([], o.children[s3])

        # properties and default values
        s = o.add_step(undo=False, action_template=self.at)
        self.assertEqual(True, s.stop_on_error)
        self.assertEqual('code to run', s.code)
        self.assertEqual('expected output', s.expected_stdout)
        s.expected_output = 'changed'
        self.assertEqual('changed', s.expected_output)
        s.expected_output = ''
        self.assertEqual('', s.expected_output)
        s.expected_output = None
        self.assertEqual('expected output', s.expected_stdout)
        self.assertEqual(0, s.expected_rc)
        s.expected_rc = 2
        self.assertEqual(2, s.expected_rc)
        s.expected_rc = 0
        self.assertEqual(0, s.expected_rc)
        s.expected_rc = None
        self.assertEqual(0, s.expected_rc)
    def fill_database(self):
        at1 = ActionTemplate(id='aaaaaaaa-1234-5678-1234-aaaaaaaa0001', name='create dir', version=1,
                             action_type=ActionType.SHELL, code='useradd {{input.user}}; mkdir {{input.dir}}',
                             expected_stdout='',
                             expected_rc=0, system_kwargs={})
        at2 = ActionTemplate(id='aaaaaaaa-1234-5678-1234-aaaaaaaa0002', name='rm dir', version=1,
                             action_type=ActionType.SHELL, code='rmuser {{input.user}}',
                             expected_stdout='',
                             expected_rc=0, system_kwargs={})
        at3 = ActionTemplate(id='aaaaaaaa-1234-5678-1234-aaaaaaaa0003', name='untar', version=1,
                             action_type=ActionType.SHELL, code='tar -xf {{input.dir}}',
                             expected_stdout='',
                             expected_rc=0, system_kwargs={})
        at4 = ActionTemplate(id='aaaaaaaa-1234-5678-1234-aaaaaaaa0004', name='install tibero',
                             version=1,
                             action_type=ActionType.SHELL, code='{{input.home}}/install_tibero.sh',
                             expected_stdout='',
                             expected_rc=0, system_kwargs={})

        o = Orchestration('Test Orchestration', 1, 'description',
                          id='bbbbbbbb-1234-5678-1234-bbbbbbbb0001')
        db.session.add(o)

        # Orch diagram
        # f   f
        # 1-->3-->u4-->u5-->u6
        #  \/  \    \_____/
        #  /\   \
        # 9-->u2 7-->u8
        # b

        s1 = o.add_step(id='dddddddd-1234-5678-1234-dddddddd0001', undo=False, action_template=at1,
                        parents=[], target=['frontend'])
        s2 = o.add_step(id='dddddddd-1234-5678-1234-dddddddd0002', undo=True, action_template=at2,
                        parents=[s1], target=[])
        s3 = o.add_step(id='dddddddd-1234-5678-1234-dddddddd0003', undo=False, action_template=at3,
                        parents=[s1], target=['frontend'])
        s4 = o.add_step(id='dddddddd-1234-5678-1234-dddddddd0004', undo=True, action_template=at2,
                        parents=[s3], target=[])
        s5 = o.add_step(id='dddddddd-1234-5678-1234-dddddddd0005', undo=True, action_template=at2,
                        parents=[s4], target=[])
        s6 = o.add_step(id='dddddddd-1234-5678-1234-dddddddd0006', undo=True, action_template=at2,
                        parents=[s4, s5], target=[])
        s7 = o.add_step(id='dddddddd-1234-5678-1234-dddddddd0007', undo=False, action_template=at4,
                        parents=[s3], target=[])
        s8 = o.add_step(id='dddddddd-1234-5678-1234-dddddddd0008', undo=True, action_template=at2,
                        parents=[s7], target=[])
        s9 = o.add_step(id='dddddddd-1234-5678-1234-dddddddd0009', undo=False, action_template=at1,
                        children=[s2, s3], target=['backend'])
    def test_create_cmd_from_orchestration(self):
        at = ActionTemplate(id='aaaaaaaa-1234-5678-1234-aaaaaaaa0001',
                            name='create dir',
                            version=1,
                            action_type=ActionType.SHELL,
                            code='mkdir {{dir}}',
                            expected_stdout='',
                            expected_rc=0,
                            system_kwargs={})

        o = Orchestration('Test Orchestration',
                          1,
                          'description',
                          id='bbbbbbbb-1234-5678-1234-bbbbbbbb0001')

        me = Server('me',
                    port=5000,
                    me=True,
                    id='cccccccc-1234-5678-1234-cccccccc0001')
        remote = Server('remote',
                        port=5000,
                        id='cccccccc-1234-5678-1234-cccccccc0002')

        db.session.add_all([me, remote, o])

        s1 = o.add_step(id='eeeeeeee-1234-5678-1234-eeeeeeee0001',
                        undo=False,
                        action_template=at,
                        parents=[],
                        target=['frontend'])
        s2 = o.add_step(id='eeeeeeee-1234-5678-1234-eeeeeeee0002',
                        undo=True,
                        action_template=at,
                        parents=[s1],
                        stop_on_error=False,
                        target=[])
        s3 = o.add_step(id='eeeeeeee-1234-5678-1234-eeeeeeee0003',
                        undo=False,
                        action_template=at,
                        parents=[s1],
                        stop_on_error=False,
                        stop_undo_on_error=False,
                        target=['frontend'])
        s4 = o.add_step(id='eeeeeeee-1234-5678-1234-eeeeeeee0004',
                        undo=True,
                        action_template=at,
                        parents=[s3],
                        target=[])
        s5 = o.add_step(id='eeeeeeee-1234-5678-1234-eeeeeeee0005',
                        undo=True,
                        action_template=at,
                        parents=[s4],
                        stop_on_error=True,
                        target=[])
        s6 = o.add_step(id='eeeeeeee-1234-5678-1234-eeeeeeee0006',
                        undo=True,
                        action_template=at,
                        parents=[s4, s5],
                        target=[])
        s7 = o.add_step(id='eeeeeeee-1234-5678-1234-eeeeeeee0007',
                        undo=False,
                        action_template=at,
                        parents=[s3],
                        undo_on_error=False,
                        target=[])
        s8 = o.add_step(id='eeeeeeee-1234-5678-1234-eeeeeeee0008',
                        undo=True,
                        action_template=at,
                        parents=[s7],
                        target=[])
        s9 = o.add_step(id='eeeeeeee-1234-5678-1234-eeeeeeee0009',
                        undo=False,
                        action_template=at,
                        children=[s2, s3],
                        target=['backend'])

        cc = create_cmd_from_orchestration(o,
                                           Context({'dir': 'C:\\test_folder'}),
                                           hosts={
                                               'all': [me.id, remote.id],
                                               'frontend': [me.id],
                                               'backend': [remote.id]
                                           },
                                           executor=None,
                                           register=mock.Mock())

        c1, c9 = cc._dag.get_nodes_at_level(1)
        self.assertTupleEqual(('cccccccc-1234-5678-1234-cccccccc0001',
                               'eeeeeeee-1234-5678-1234-eeeeeeee0001'), c1.id)
        self.assertIsInstance(c1, Command)
        self.assertTrue(c1.stop_on_error)
        self.assertTrue(c1.undo_on_error)
        self.assertIsNone(c1.stop_undo_on_error)

        self.assertTupleEqual(('cccccccc-1234-5678-1234-cccccccc0002',
                               'eeeeeeee-1234-5678-1234-eeeeeeee0009'), c9.id)
        self.assertIsInstance(c9, ProxyCommand)
        self.assertTrue(c9.stop_on_error)
        self.assertTrue(c9.undo_on_error)
        self.assertIsNone(c9.stop_undo_on_error)

        c21 = c1.undo_command
        self.assertTupleEqual(('cccccccc-1234-5678-1234-cccccccc0001',
                               'eeeeeeee-1234-5678-1234-eeeeeeee0002'), c21.id)
        self.assertIsInstance(c21, UndoCommand)
        self.assertFalse(c21.stop_on_error)

        c22 = c9.undo_command
        self.assertTupleEqual(('cccccccc-1234-5678-1234-cccccccc0002',
                               'eeeeeeee-1234-5678-1234-eeeeeeee0002'), c22.id)
        self.assertIsInstance(c22, ProxyUndoCommand)
        self.assertFalse(c22.stop_on_error)

        c3, = cc._dag.get_nodes_at_level(2)
        self.assertTupleEqual(('cccccccc-1234-5678-1234-cccccccc0001',
                               'eeeeeeee-1234-5678-1234-eeeeeeee0003'), c3.id)
        self.assertIsInstance(c3, Command)
        self.assertFalse(c3.stop_on_error)
        self.assertTrue(c3.undo_on_error)
        self.assertFalse(c3.stop_undo_on_error)

        self.assertTupleEqual(('undo', 'eeeeeeee-1234-5678-1234-eeeeeeee0003'),
                              c3.undo_command.id)
        self.assertIsInstance(c3.undo_command, CompositeCommand)
        self.assertFalse(c3.undo_command.stop_on_error)
        self.assertIsNone(c3.undo_command.stop_undo_on_error)

        c4, = c3.undo_command._dag.get_nodes_at_level(1)
        self.assertIsInstance(c4, UndoCommand)
        self.assertFalse(c4.stop_on_error)

        c5, = c3.undo_command._dag.get_nodes_at_level(2)
        self.assertIsInstance(c5, UndoCommand)
        self.assertTrue(c5.stop_on_error)

        c6, = c3.undo_command._dag.get_nodes_at_level(3)
        self.assertIsInstance(c6, UndoCommand)
        self.assertFalse(c6.stop_on_error)

        cc7, = cc._dag.get_nodes_at_level(3)
        self.assertEqual('eeeeeeee-1234-5678-1234-eeeeeeee0007', cc7.id)
        self.assertIsInstance(cc7, CompositeCommand)
        self.assertFalse(cc7.stop_on_error)
        self.assertFalse(cc7.stop_undo_on_error)

        c71, c72 = cc7._dag.root
        self.assertTupleEqual(('cccccccc-1234-5678-1234-cccccccc0001',
                               'eeeeeeee-1234-5678-1234-eeeeeeee0007'), c71.id)
        self.assertIsInstance(c71, Command)
        self.assertTrue(c71.stop_on_error)
        self.assertFalse(c71.undo_on_error)
        self.assertIsNone(c71.stop_undo_on_error)

        self.assertTupleEqual(('cccccccc-1234-5678-1234-cccccccc0001',
                               'eeeeeeee-1234-5678-1234-eeeeeeee0008'),
                              c71.undo_command.id)
        self.assertIsInstance(c71.undo_command, UndoCommand)
        self.assertTrue(c71.undo_command.stop_on_error)

        self.assertTupleEqual(('cccccccc-1234-5678-1234-cccccccc0002',
                               'eeeeeeee-1234-5678-1234-eeeeeeee0007'), c72.id)
        self.assertIsInstance(c72, ProxyCommand)
        self.assertTrue(c72.stop_on_error)
        self.assertFalse(c72.undo_on_error)
        self.assertIsNone(c72.stop_undo_on_error)

        self.assertTupleEqual(('cccccccc-1234-5678-1234-cccccccc0002',
                               'eeeeeeee-1234-5678-1234-eeeeeeee0008'),
                              c72.undo_command.id)
        self.assertIsInstance(c72.undo_command, ProxyUndoCommand)
        self.assertTrue(c72.undo_command.stop_on_error)
Exemple #23
0
    def test_eq_imp(self):
        o1 = Orchestration('dto', 1)

        s11 = o1.add_step(False, self.at)
        s12 = o1.add_step(False, self.at, parents=[s11])

        o2 = Orchestration('dto', 2)
        s21 = o2.add_step(
            False,
            self.at,
        )
        s22 = o2.add_step(False, self.at, parents=[s21])

        self.assertTrue(o1.eq_imp(o2))
        self.assertTrue(o2.eq_imp(o1))

        s23 = o2.add_step(True, self.at, parents=[s22])

        self.assertFalse(o1.eq_imp(o2))
        self.assertFalse(o2.eq_imp(o1))

        s13 = o1.add_step(True, self.at, parents=[s12])

        self.assertTrue(o1.eq_imp(o2))

        self.assertTrue(s13.eq_imp(s23))
        self.assertTrue(o1.eq_imp(o2))
Exemple #24
0
    def test_schema(self):
        self.maxDiff = None
        o = Orchestration('Schema Orch', 1)
        s1 = o.add_step(id=1,
                        action_type=ActionType.SHELL,
                        undo=False,
                        schema={
                            'input': {
                                '1_a': {},
                                '1_b': {}
                            },
                            'required': ['1_b'],
                            'output': ['1_c']
                        })
        s2 = o.add_step(undo=False,
                        action_type=ActionType.SHELL,
                        parents=[s1],
                        schema={
                            'input': {
                                '2_a': {}
                            },
                            'required': ['2_a'],
                            'mapping': {
                                '2_a': {
                                    'from': '1_c'
                                }
                            }
                        })

        self.assertDictEqual(
            {
                'input': {
                    '1_a': {},
                    '1_b': {}
                },
                'required': ['input.1_b'],
                'output': ['1_c']
            }, o.schema)

        o = Orchestration('Schema Orch',
                          1,
                          id='00000000-0000-0000-0000-000000000001')
        s1 = o.add_step(id=1,
                        action_type=ActionType.SHELL,
                        undo=False,
                        schema={
                            'input': {
                                '1_a': {},
                                '1_b': {}
                            },
                            'required': ['1_b'],
                            'output': ['1_c']
                        })
        s2 = o.add_step(undo=False,
                        action_type=ActionType.SHELL,
                        schema={
                            'input': {
                                '2_a': {}
                            },
                            'required': ['2_a'],
                            'output': ['2_b']
                        })

        s3 = o.add_step(undo=False,
                        action_type=ActionType.SHELL,
                        parents=[s1],
                        schema={
                            'input': {
                                '3_a': {},
                                '3_b': {}
                            },
                            'required': ['3_a'],
                            'mapping': {
                                '3_a': {
                                    'from': '2_b'
                                }
                            }
                        })

        self.assertDictEqual(
            {
                'input': {
                    '1_a': {},
                    '1_b': {},
                    '2_a': {},
                    '3_b': {}
                },
                'required': ['input.1_b', 'input.2_a'],
                'output': ['1_c', '2_b']
            }, o.schema)

        db.session.add(o)
        o2 = Orchestration('Schema Orch', 1)
        at = ActionTemplate.query.filter_by(name='orchestration',
                                            version=1).one()
        s1 = o2.add_step(id=1,
                         action_template=at,
                         undo=False,
                         schema={'mapping': {
                             'orchestration': o.id
                         }})
        s2 = o2.add_step(undo=False,
                         action_type=1,
                         parents=[s1],
                         schema={
                             'input': {
                                 '1': {},
                                 '2': {}
                             },
                             'required': ['1', '2'],
                             'mapping': {
                                 '1': {
                                     'from': '1_c'
                                 },
                                 '2': {
                                     'from': '5'
                                 }
                             }
                         })

        with self.assertRaises(errors.MappingError):
            self.assertDictEqual(
                {
                    'input': {
                        'hosts': at.schema['input']['hosts'],
                        '1_a': {},
                        '1_b': {},
                        '2_a': {},
                        '3_b': {}
                    },
                    'required': ['input.1_b', 'input.2_a', 'input.hosts'],
                    'output': ['1_c', '2_b']
                }, o2.schema)

        s2.schema = {
            'input': {
                '1': {},
                '2': {}
            },
            'required': ['1'],
            'mapping': {
                '1': {
                    'from': 'env.server_id'
                },
                '2': {
                    'from': '5'
                }
            }
        }

        self.assertDictEqual(
            {
                'input': {
                    'hosts': at.schema['input']['hosts'],
                    'version': {
                        'type': 'integer'
                    },  # from ActionTemplate
                    '1_a': {},
                    '1_b': {},
                    '2_a': {},
                    '3_b': {}
                },
                'required': ['input.1_b', 'input.2_a', 'input.hosts'],
                'output': ['1_c', '2_b']
            },
            o2.schema)

        # test a container required in step
        o = Orchestration('Schema Orch', 1)
        s1 = o.add_step(id=1,
                        action_type=ActionType.SHELL,
                        undo=False,
                        schema={
                            'container': {
                                '1_a': {}
                            },
                            'required': ['container.1_a']
                        })
        s2 = o.add_step(id=1,
                        action_type=ActionType.SHELL,
                        undo=False,
                        schema={
                            'input': {
                                '1_b': {}
                            },
                            'mapping': {
                                '1_b': {
                                    'from': 'container.foo'
                                }
                            },
                            'required': ['1_b']
                        },
                        parents=[s1])

        self.assertDictEqual(
            {
                'container': {
                    '1_a': {}
                },
                'required': ['container.1_a', 'container.foo']
            }, o.schema)
    def test_sqlalchemy_entity_events(self, mock_now):
        mock_now.return_value = dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc)

        o = Orchestration('orch',
                          1,
                          last_modified_at=dt.datetime(2019,
                                                       4,
                                                       1,
                                                       tzinfo=dt.timezone.utc))
        self.assertEqual(dt.datetime(2019, 4, 1, tzinfo=dt.timezone.utc),
                         o.last_modified_at)
        db.session.add(o)
        self.assertEqual(dt.datetime(2019, 4, 1, tzinfo=dt.timezone.utc),
                         o.last_modified_at)
        db.session.commit()
        self.assertEqual(dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc),
                         o.last_modified_at)

        c = Catalog.query.get('Orchestration')
        self.assertEqual(dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc),
                         c.last_modified_at)

        mock_now.return_value = dt.datetime(2019, 4, 3, tzinfo=dt.timezone.utc)

        o.name = 'modified'
        self.assertEqual(dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc),
                         o.last_modified_at)
        db.session.flush()
        self.assertEqual(dt.datetime(2019, 4, 3, tzinfo=dt.timezone.utc),
                         o.last_modified_at)
        db.session.commit()
        self.assertEqual(dt.datetime(2019, 4, 3, tzinfo=dt.timezone.utc),
                         o.last_modified_at)

        c = Catalog.query.get('Orchestration')
        self.assertEqual(dt.datetime(2019, 4, 3, tzinfo=dt.timezone.utc),
                         c.last_modified_at)

        mock_now.return_value = dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc)
        o2 = Orchestration('orch',
                           2,
                           last_modified_at=dt.datetime(
                               2019, 4, 1, tzinfo=dt.timezone.utc))
        db.session.add(o2)
        self.assertEqual(dt.datetime(2019, 4, 1, tzinfo=dt.timezone.utc),
                         o2.last_modified_at)

        with bypass_datamark_update():
            self.assertEqual(dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc),
                             o2.last_modified_at)

            o3 = Orchestration('orch',
                               3,
                               last_modified_at=dt.datetime(
                                   2019, 4, 4, tzinfo=dt.timezone.utc))
            db.session.add(o3)
            o.name = 'orch'

            self.assertEqual(dt.datetime(2019, 4, 3, tzinfo=dt.timezone.utc),
                             o.last_modified_at)
            self.assertEqual(dt.datetime(2019, 4, 4, tzinfo=dt.timezone.utc),
                             o3.last_modified_at)

        self.assertEqual(dt.datetime(2019, 4, 3, tzinfo=dt.timezone.utc),
                         o.last_modified_at)
        self.assertEqual(dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc),
                         o2.last_modified_at)
        self.assertEqual(dt.datetime(2019, 4, 4, tzinfo=dt.timezone.utc),
                         o3.last_modified_at)
        db.session.commit()
        self.assertEqual(dt.datetime(2019, 4, 3, tzinfo=dt.timezone.utc),
                         o.last_modified_at)
        self.assertEqual(dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc),
                         o2.last_modified_at)
        self.assertEqual(dt.datetime(2019, 4, 4, tzinfo=dt.timezone.utc),
                         o3.last_modified_at)

        c = Catalog.query.get('Orchestration')
        self.assertEqual(dt.datetime(2019, 4, 4, tzinfo=dt.timezone.utc),
                         c.last_modified_at)
Exemple #26
0
    def _execute(self,
                 params: Kwargs,
                 timeout=None,
                 context: Context = None) -> CompletedProcess:
        from dimensigon.use_cases.deployment import deploy_orchestration
        input_params = params['input']
        cp = CompletedProcess().set_start_time()
        # Validation
        orchestration = Orchestration.get(
            input_params.pop('orchestration', None),
            input_params.pop('version', None))
        if not isinstance(orchestration, Orchestration):
            cp.stderr = orchestration
            cp.success = False
            return cp.set_end_time()

        hosts = input_params.pop('hosts', None)
        if hosts is None:
            cp.stderr = "No hosts specified"
            cp.success = False
            return cp.set_end_time()
        hosts = copy.deepcopy(hosts)
        if isinstance(hosts, list):
            hosts = {'all': hosts}
        elif isinstance(hosts, str):
            hosts = {'all': [hosts]}
        not_found = normalize_hosts(hosts)
        if not_found:
            cp.stderr = str(errors.ServerNormalizationError(not_found))
            cp.success = False
            return cp.set_end_time()

        o_exe = OrchExecution(
            orchestration_id=orchestration.id,
            target=hosts,
            params=input_params,
            executor_id=context.env.get('executor_id'),
            server_id=context.env.get('server_id'),
            parent_step_execution_id=context.env.get('step_execution_id'))
        db.session.add(o_exe)
        se = StepExecution.query.get(context.env.get('step_execution_id'))
        if se:
            se.child_orch_execution_id = o_exe.id
        db.session.commit()

        cp.stdout = f"orch_execution_id={o_exe.id}"

        env = dict(context.env)
        env.update(
            root_orch_execution_id=context.env['root_orch_execution_id'],
            orch_execution_id=o_exe.id,
            executor_id=context.env['executor_id'])
        ctx = Context(input_params, env, vault=context.vault)
        try:
            deploy_orchestration(orchestration=orchestration,
                                 hosts=hosts,
                                 var_context=ctx,
                                 execution=o_exe,
                                 timeout=timeout)
        except Exception as e:
            cp.stderr = str(e) if str(e) else e.__class__.__name__
            cp.success = False
        else:
            context.merge_ctx(ctx)
            db.session.refresh(o_exe)
            cp.success = o_exe.success
        return cp.set_end_time()
class TestLaunchOrchestration(TestDimensigonBase, ValidateResponseMixin):

    def fill_database(self):
        self.o = Orchestration('create user', version=1)
        self.at1 = ActionTemplate(action_type=ActionType.SHELL, name="create folder", version=1,
                                  code="sudo mkdir -p {{folder}}")
        self.at2 = ActionTemplate(action_type=ActionType.SHELL, name="delete folder", version=1,
                                  code="sudo rm -fr {{folder}}")
        self.at3 = ActionTemplate(action_type=ActionType.SHELL, name="create user", version=1,
                                  code="sudo useradd -d {{home}} {{user}}")
        self.at4 = ActionTemplate(action_type=ActionType.SHELL, name="delete user", version=1,
                                  code="sudo userdel {{user}}")

        self.st1 = self.o.add_step(undo=False, action_template=self.at1)
        self.st2 = self.o.add_step(undo=True, action_template=self.at2, parents=[self.st1])
        self.st3 = self.o.add_step(undo=False, action_template=self.at3, parents=[self.st1])
        self.st4 = self.o.add_step(undo=True, action_template=self.at4, parents=[self.st3])
        db.session.add(self.o)

        self.o2 = Orchestration('create user', version=2)
        self.st21 = self.o2.add_step(undo=False, action_template=self.at1)
        db.session.add(self.o2)

        db.session.commit()

    def test_launch_orchestration_error_on_server(self):
        resp = self.client.post(url_for('api_1_0.launch_orchestration', orchestration_id=str(self.o.id)),
                                json={'hosts': 'aaaaaaaa-1234-5678-1234-56781234aaa1'},
                                headers=self.auth.header)

        self.assertEqual(404, resp.status_code)
        self.assertDictEqual(
            errors.format_error_content(errors.ServerNormalizationError(['aaaaaaaa-1234-5678-1234-56781234aaa1'])),
            resp.get_json())

    def test_launch_orchestration_error_on_target(self):
        resp = self.client.post(url_for('api_1_0.launch_orchestration', orchestration_id=str(self.o.id)),
                                json={'hosts': {'all': ['aaaaaaaa-1234-5678-1234-56781234aaa1'],
                                                'other': 'granule'}},
                                headers=self.auth.header)

        self.validate_error_response(resp, errors.TargetNotNeeded(['other']))

        s = self.o.steps[0]
        s.target = ['new']
        db.session.commit()

        resp = self.client.post(url_for('api_1_0.launch_orchestration', orchestration_id=str(self.o.id)),
                                json={'hosts': {'all': ['aaaaaaaa-1234-5678-1234-56781234aaa1']}},
                                headers=self.auth.header)

        self.validate_error_response(resp, errors.TargetUnspecified(['new']))

    @patch('dimensigon.web.api_1_0.urls.use_cases.uuid.uuid4')
    @patch('dimensigon.web.api_1_0.urls.use_cases.Context')
    @patch('dimensigon.web.api_1_0.urls.use_cases.deploy_orchestration')
    def test_launch_orchestration_ok_foreground(self, mock_deploy, mock_var_context, mock_uuid4):
        mock_uuid4.return_value = 'a7083c43-34cc-4b26-91f0-ea0928cf5945'

        data = {'hosts': self.s1.id,
                'params': {'folder': '/home/{{user}}',
                           'home': '{{folder}}',
                           'user': '******'},
                'skip_validation': True,
                'background': False}

        oe = OrchExecution(id=mock_uuid4.return_value, orchestration_id=self.o.id)
        db.session.add(oe)
        db.session.commit()

        def deploy_orchestration(*args, **kwargs):
            return mock_uuid4.return_value

        mock_deploy.side_effect = deploy_orchestration
        MockVarContext = mock.Mock(name='MockVarContext')
        mock_var_context.return_value = MockVarContext
        resp = self.client.post(url_for('api_1_0.launch_orchestration', orchestration_id=str(self.o.id)),
                                json=data,
                                headers=self.auth.header)

        self.assertEqual(200, resp.status_code)
        self.assertDictEqual(oe.to_json(add_step_exec=True, split_lines=True), resp.get_json())

        mock_var_context.assert_called_once_with(data['params'],
                                                 dict(execution_id=None, root_orch_execution_id=mock_uuid4.return_value,
                                                      orch_execution_id=mock_uuid4.return_value,
                                                      executor_id=ROOT),
                                                 vault={})

        mock_deploy.assert_called_once_with(orchestration=self.o, var_context=MockVarContext,
                                            hosts={'all': [self.s1.id]}, timeout=None)

    @patch('dimensigon.web.api_1_0.urls.use_cases.uuid.uuid4')
    @patch('dimensigon.web.api_1_0.urls.use_cases.Context')
    @patch('dimensigon.web.api_1_0.urls.use_cases.executor.submit')
    def test_launch_orchestration_ok_background(self, mock_submit, mock_var_context, mock_uuid4):
        with self.subTest("Test background resolved"):
            mock_uuid4.return_value = 'a7083c43-34cc-4b26-91f0-ea0928cf5945'

            data = {'hosts': self.s1.id,
                    'params': {'folder': '/home/{{user}}',
                               'home': '{{folder}}',
                               'user': '******'},
                    'skip_validation': True,
                    'background': True}

            oe = OrchExecution(id=mock_uuid4.return_value, orchestration_id=self.o.id)
            db.session.add(oe)
            db.session.commit()

            mock_submit.return_value = mock.Mock()

            MockVarContext = mock.Mock(name='MockVarContext')
            mock_var_context.return_value = MockVarContext

            resp = self.client.post(url_for('api_1_0.launch_orchestration', orchestration_id=str(self.o.id)),
                                    json=data,
                                    headers=self.auth.header)

            mock_var_context.assert_called_once_with(data['params'],
                                                     dict(execution_id=None,
                                                          root_orch_execution_id=mock_uuid4.return_value,
                                                          orch_execution_id=mock_uuid4.return_value,
                                                          executor_id=ROOT),
                                                     vault={})

            mock_submit.assert_called_once_with(deploy_orchestration, orchestration=self.o.id,
                                                var_context=MockVarContext,
                                                hosts={'all': [self.s1.id]}, timeout=None)

            self.assertEqual(200, resp.status_code)
            self.assertDictEqual(oe.to_json(add_step_exec=True, split_lines=True), resp.get_json())

        with self.subTest("Test background taking longer"):
            mock_submit.return_value = mock.Mock()
            mock_submit.return_value.result.side_effect = concurrent.futures.TimeoutError()

            resp = self.client.post(url_for('api_1_0.launch_orchestration', orchestration_id=str(self.o.id)),
                                    json=data,
                                    headers=self.auth.header)

            self.assertEqual(202, resp.status_code)
            self.assertDictEqual({'execution_id': 'a7083c43-34cc-4b26-91f0-ea0928cf5945'},
                                 resp.get_json())

    @patch('dimensigon.web.api_1_0.urls.use_cases.uuid.uuid4')
    @patch('dimensigon.web.api_1_0.urls.use_cases.Context')
    @patch('dimensigon.web.api_1_0.urls.use_cases.deploy_orchestration')
    def test_launch_orchestration_filter_by_name(self, mock_deploy, mock_var_context, mock_uuid4):
        mock_uuid4.return_value = 'a7083c43-34cc-4b26-91f0-ea0928cf5945'

        data = {'hosts': self.s1.id,
                'orchestration': self.o.name,
                'params': {'folder': '/home/{{user}}',
                           'home': '{{folder}}',
                           'user': '******'},
                'skip_validation': True,
                'background': False}

        oe = OrchExecution(id=mock_uuid4.return_value, orchestration_id=self.o2.id)
        db.session.add(oe)
        db.session.commit()

        def deploy_orchestration(*args, **kwargs):
            return mock_uuid4.return_value

        mock_deploy.side_effect = deploy_orchestration
        MockVarContext = mock.Mock(name='MockVarContext')
        mock_var_context.return_value = MockVarContext
        resp = self.client.post(url_for('api_1_0.launch_orchestration'),
                                json=data,
                                headers=self.auth.header)

        self.assertEqual(200, resp.status_code)
        self.assertDictEqual(oe.to_json(add_step_exec=True, split_lines=True), resp.get_json())

        mock_var_context.assert_called_once_with(data['params'],
                                                 dict(execution_id=None,
                                                      root_orch_execution_id=mock_uuid4.return_value,
                                                      orch_execution_id=mock_uuid4.return_value,
                                                      executor_id=ROOT),
                                                 vault={})

        mock_deploy.assert_called_once_with(orchestration=self.o2, var_context=MockVarContext,
                                            hosts={'all': [self.s1.id]}, timeout=None)

    @patch('dimensigon.web.api_1_0.urls.use_cases.uuid.uuid4')
    @patch('dimensigon.web.api_1_0.urls.use_cases.Context')
    @patch('dimensigon.web.api_1_0.urls.use_cases.deploy_orchestration')
    def test_launch_orchestration_filter_by_name_version(self, mock_deploy, mock_var_context, mock_uuid4):
        mock_uuid4.return_value = 'a7083c43-34cc-4b26-91f0-ea0928cf5945'

        data = {'hosts': self.s1.id,
                'orchestration': self.o.name,
                'version': 1,
                'params': {'folder': '/home/{{user}}',
                           'home': '{{folder}}',
                           'user': '******'},
                'skip_validation': True,
                'background': False}

        oe = OrchExecution(id=mock_uuid4.return_value, orchestration_id=self.o.id)
        db.session.add(oe)
        db.session.commit()

        def deploy_orchestration(*args, **kwargs):
            return mock_uuid4.return_value

        mock_deploy.side_effect = deploy_orchestration
        MockVarContext = mock.Mock(name='MockVarContext')
        mock_var_context.return_value = MockVarContext
        resp = self.client.post(url_for('api_1_0.launch_orchestration'),
                                json=data,
                                headers=self.auth.header)

        self.assertEqual(200, resp.status_code)
        self.assertDictEqual(oe.to_json(add_step_exec=True, split_lines=True), resp.get_json())

        mock_var_context.assert_called_once_with(data['params'],
                                                 dict(execution_id=None,
                                                      root_orch_execution_id=mock_uuid4.return_value,
                                                      orch_execution_id=mock_uuid4.return_value,
                                                      executor_id=ROOT),
                                                 vault={})

        mock_deploy.assert_called_once_with(orchestration=self.o, var_context=MockVarContext,
                                            hosts={'all': [self.s1.id]}, timeout=None)

    def test_launch_orchestration_filter_by_orchestration_not_found(self):
        data = {'hosts': self.s1.id,
                'orchestration': 'invented',
                'params': {'folder': '/home/{{user}}',
                           'home': '{{folder}}',
                           'user': '******'},
                'skip_validation': True,
                'background': False}

        resp = self.client.post(url_for('api_1_0.launch_orchestration'),
                                json=data,
                                headers=self.auth.header)

        self.validate_error_response(resp, errors.EntityNotFound('Orchestration', ['invented'], ('orchestration',)))

    def test_launch_orchestration_filter_by_version_not_found(self):
        data = {'hosts': self.s1.id,
                'orchestration': self.o.name,
                'version': 3,
                'params': {'folder': '/home/{{user}}',
                           'home': '{{folder}}',
                           'user': '******'},
                'skip_validation': True,
                'background': False}

        resp = self.client.post(url_for('api_1_0.launch_orchestration'),
                                json=data,
                                headers=self.auth.header)

        self.validate_error_response(resp, errors.EntityNotFound('Orchestration', [self.o.name, 3],
                                                                 ['orchestration', 'version']))
class Test(TestResourceBase):
    def fill_database(self):
        self.at = ActionTemplate(name='action_name',
                                 version=1,
                                 action_type=ActionType.SHELL,
                                 code='')
        self.o = Orchestration(name='name', version=1)
        self.st1 = self.o.add_step(undo=False, action_template=self.at)
        self.st2 = self.o.add_step(undo=False,
                                   action_template=self.at,
                                   children=[self.st1])
        self.st3 = self.o.add_step(undo=False,
                                   action_template=self.at,
                                   children=[self.st2])
        db.session.add_all([self.at, self.o])

    def tearDown(self) -> None:
        db.session.remove()
        db.drop_all()
        self.app_context.pop()

    def test_get(self):
        resp = self.client.get(url_for('api_1_0.steprelationshipchildren',
                                       step_id=str(self.st1.id)),
                               headers=self.auth.header)

        self.assertEqual(200, resp.status_code)
        self.assertDictEqual(dict(child_step_ids=[]), resp.get_json())

        resp = self.client.get(url_for('api_1_0.steprelationshipchildren',
                                       step_id=str(self.st2.id)),
                               headers=self.auth.header)

        self.assertEqual(200, resp.status_code)
        self.assertDictEqual(dict(child_step_ids=[str(self.st1.id)]),
                             resp.get_json())

    def test_patch(self):
        resp = self.client.patch(url_for('api_1_0.steprelationshipchildren',
                                         step_id=str(self.st3.id)),
                                 json={'child_step_ids': [str(self.st1.id)]},
                                 headers=self.auth.header)

        self.assertEqual(200, resp.status_code)
        self.assertDictEqual(dict(child_step_ids=[str(self.st1.id)]),
                             resp.get_json())
        self.assertListEqual([self.st1], self.st3.children)

    def test_post(self):
        resp = self.client.post(url_for('api_1_0.steprelationshipchildren',
                                        step_id=str(self.st3.id)),
                                json={'child_step_ids': [str(self.st1.id)]},
                                headers=self.auth.header)

        self.assertEqual(200, resp.status_code)
        expected = [str(self.st2.id), str(self.st1.id)]
        expected.sort()
        actual = resp.get_json()['child_step_ids']
        actual.sort()
        self.assertListEqual(expected, actual)
        self.assertIn(self.st1, self.st3.children)
        self.assertIn(self.st2, self.st3.children)

    def test_delete(self):
        resp = self.client.delete(url_for('api_1_0.steprelationshipchildren',
                                          step_id=str(self.st3.id)),
                                  json={'child_step_ids': [str(self.st2.id)]},
                                  headers=self.auth.header)

        self.assertEqual(200, resp.status_code)
        self.assertDictEqual(dict(child_step_ids=[]), resp.get_json())
        self.assertEqual(0, len(self.st3.children))
Exemple #29
0
def orchestrations_full():
    json_data = request.get_json()
    json_steps = json_data.pop('steps')
    generated_version = False
    if 'version' not in json_data:
        generated_version = True
        json_data['version'] = Orchestration.query.filter_by(
            name=json_data['name']).count() + 1
    o = Orchestration(**json_data)
    db.session.add(o)
    resp_data = {'id': o.id}
    if generated_version:
        resp_data.update(version=o.version)

    # reorder steps in order of dependency
    id2step = {s['id']: s for s in json_steps}

    dag = DAG()
    for s in json_steps:
        step_id = s['id']
        if s['undo'] and len(s.get('parent_step_ids', [])) == 0:
            raise errors.UndoStepWithoutParent(step_id)
        dag.add_node(step_id)
        for p_s_id in s.get('parent_step_ids', []):
            dag.add_edge(p_s_id, step_id)

    if dag.is_cyclic():
        raise errors.CycleError

    new_steps = []
    for step_id in dag.ordered_nodes:
        step = id2step[step_id]
        new_steps.append(step)
    # end reorder steps in order of dependency

    rid2step = OrderedDict()
    dependencies = {}
    for json_step in new_steps:
        rid = json_step.pop('id', None)
        json_step['rid'] = rid
        if rid is not None and rid in rid2step.keys():
            raise errors.DuplicatedId(rid)
        if 'action_template_id' in json_step:
            json_step['action_template'] = ActionTemplate.query.get_or_raise(
                json_step.pop('action_template_id'))
        elif 'action_type' in json_step:
            json_step['action_type'] = ActionType[json_step.pop('action_type')]
        dependencies[rid] = {
            'parent_step_ids':
            [p_id for p_id in json_step.pop('parent_step_ids', [])]
        }
        s = o.add_step(**json_step)
        db.session.add(s)
        if rid:
            rid2step[rid] = s

        continue

    # process dependencies
    for rid, dep in dependencies.items():
        step = rid2step[rid]
        parents = []
        for p_s_id in dep['parent_step_ids']:
            if p_s_id in rid2step:
                parents.append(rid2step[p_s_id])
        o.set_parents(step, parents)

    db.session.commit()

    # send new ids in order of appearance at beginning
    new_id_steps = []
    for rid in rid2step.keys():
        new_id_steps.append(rid2step[rid].id)
    resp_data.update({'step_ids': new_id_steps})
    return resp_data, 201
Exemple #30
0
    def test_to_from_json(self):
        start = dt.datetime(2019, 4, 1, tzinfo=dt.timezone.utc)
        end = dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc)
        u = User('user', id='cccccccc-1234-5678-1234-56781234ccc1')
        db.session.add(u)
        o = Orchestration('run_orch', 1, id='eeeeeeee-1234-5678-1234-56781234eee1')
        s = o.add_step(undo=False,
                       action_template=ActionTemplate('orchestration', 1, ActionType.ORCHESTRATION, code=''))

        co = Orchestration('child', 1, id='eeeeeeee-1234-5678-1234-56781234eee2')
        cs = o.add_step(undo=False, action_template=ActionTemplate('action', 1, ActionType.SHELL, code=''))

        db.session.add_all([o, s, co, cs])

        oe = OrchExecution(id='bbbbbbbb-1234-5678-1234-56781234bbb1', start_time=start,
                           end_time=end,
                           target={'all': [str(self.me.id), str(self.remote.id)], 'backend': self.remote.name},
                           params={'params': 'content'},
                           executor=u,
                           orchestration=o
                           )
        se = StepExecution(id='aaaaaaaa-1234-5678-1234-56781234aaa1', start_time=start, end_time=end, step=s,
                           orch_execution_id=oe.id,
                           params={'orchestration_id': 'eeeeeeee-1234-5678-1234-56781234eee2'}, rc=None, success=True,
                           server=self.remote)

        coe = OrchExecution(id='bbbbbbbb-1234-5678-1234-56781234bbb2', start_time=start,
                            end_time=end,
                            target={'all': [str(self.me.id), str(self.remote.id)], 'backend': self.remote.name},
                            params={'params': 'content'},
                            executor=u,
                            orchestration=o,
                            parent_step_execution_id=se.id
                            )
        cse = StepExecution(id='aaaaaaaa-1234-5678-1234-56781234aaa2', start_time=start, end_time=end, step=s,
                            orch_execution_id=coe.id,
                            params={'param': 'data'}, rc=0, success=True, server=self.remote,
                            )

        se.child_orch_execution_id = coe.id
        db.session.add_all([oe, se, coe, cse])
        db.session.commit()
        self.assertDictEqual(dict(id=str(oe.id),
                                  start_time=start.strftime(defaults.DATETIME_FORMAT),
                                  end_time=end.strftime(defaults.DATETIME_FORMAT),
                                  target={'all': [str(self.me.id), str(self.remote.id)], 'backend': self.remote.name},
                                  params={'params': 'content'}, orchestration_id=str(o.id),
                                  success=None, undo_success=None,
                                  executor_id='cccccccc-1234-5678-1234-56781234ccc1',
                                  message=None),
                             oe.to_json())

        self.assertDictEqual(dict(id=str(oe.id),
                                  start_time=start.strftime(defaults.DATETIME_FORMAT),
                                  end_time=end.strftime(defaults.DATETIME_FORMAT),
                                  target={'all': [str(self.me), str(self.remote)], 'backend': self.remote.name},
                                  params={'params': 'content'},
                                  orchestration={'id': 'eeeeeeee-1234-5678-1234-56781234eee1',
                                                 'name': 'run_orch',
                                                 'version': 1},
                                  success=None, undo_success=None,
                                  executor='user',
                                  message=None),
                             oe.to_json(human=True))

        dumped = oe.to_json(add_step_exec=True)
        self.assertEqual(1, len(dumped['steps']))
        self.assertEqual('aaaaaaaa-1234-5678-1234-56781234aaa1', dumped['steps'][0]['id'])
        self.assertIn('orch_execution', dumped['steps'][0])

        o_e_json = oe.to_json()
        db.session.commit()
        del oe

        # load existent object
        smashed = OrchExecution.from_json(o_e_json)

        self.assertEqual('bbbbbbbb-1234-5678-1234-56781234bbb1', smashed.id)
        self.assertEqual(start, smashed.start_time)
        self.assertEqual(end, smashed.end_time)
        self.assertEqual(o.id, smashed.orchestration.id)
        self.assertDictEqual({'all': [str(self.me.id), str(self.remote.id)], 'backend': self.remote.name},
                             smashed.target)
        self.assertDictEqual({'params': 'content'}, smashed.params)
        self.assertEqual(User.get_by_name('user'), smashed.executor)
        self.assertEqual(None, smashed.service)
        self.assertEqual(None, smashed.success)
        self.assertEqual(None, smashed.undo_success)

        # load new object and insert into database
        new_obj = OrchExecution.from_json(dict(id='bbbbbbbb-1234-5678-1234-56781234bbb3',
                                               start_time=start.strftime(defaults.DATETIME_FORMAT),
                                               end_time=end.strftime(defaults.DATETIME_FORMAT),
                                               target={'all': [str(self.me.id), str(self.remote.id)],
                                                       'backend': self.remote.name},
                                               params={'params': 'content'},
                                               orchestration_id=str(o.id),
                                               service_id=None,
                                               success=None, undo_success=None,
                                               executor_id='cccccccc-1234-5678-1234-56781234ccc1'))
        db.session.add(new_obj)
        db.session.commit()