Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions f5/bigip/tm/util/Bash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# coding=utf-8
#
# Copyright 2016 F5 Networks Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""BIG-IP® utility module

REST URI
``http://localhost/mgmt/tm/util/bash``

GUI Path
N/A

REST Kind
``tm:util:bash:*``
"""

from f5.bigip.mixins import CommandExecutionMixin
from f5.bigip.resource import UnnamedResource
from f5.utils.util_exceptions import UtilError


class Bash(UnnamedResource, CommandExecutionMixin):
"""BIG-IP® utility command

.. note::

This is an unnamed resource so it has not ~Partition~Name pattern
Copy link
Copy Markdown
Contributor

@caphrim007 caphrim007 Sep 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"no" instead of "not". We will fix this in a follow-up PR

at the end of its URI.
"""

def __init__(self, util):
super(Bash, self).__init__(util)
self._meta_data['required_command_parameters'].update(('utilCmdArgs',))
self._meta_data['required_json_kind'] = 'tm:util:bash:runstate'
self._meta_data['allowed_commands'].append('run')

def _exec_cmd(self, command, **kwargs):
kwargs['command'] = command
self._check_exclusive_parameters(**kwargs)
requests_params = self._handle_requests_params(kwargs)
self._check_command_parameters(**kwargs)

if not kwargs['utilCmdArgs'].startswith("-c"):
raise UtilError(
'Required format is "-c <bash command and arguments>"')

session = self._meta_data['bigip']._meta_data['icr_session']
response = session.post(
self._meta_data['uri'], json=kwargs, **requests_params)
self._local_update(response.json())

if 'commandResult' in self.__dict__:
if self.commandResult.startswith('/bin/bash'):
raise UtilError('%s' % self.commandResult.split(' ', 1)[1])
else:
return self
else:
return self
2 changes: 2 additions & 0 deletions f5/bigip/tm/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"""

from f5.bigip.resource import PathElement
from f5.bigip.tm.util.Bash import Bash
from f5.bigip.tm.util.Unix_Ls import Unix_Ls
from f5.bigip.tm.util.Unix_Mv import Unix_Mv
from f5.bigip.tm.util.Unix_Rm import Unix_Rm
Expand All @@ -37,6 +38,7 @@ class Util(PathElement):
def __init__(self, bigip):
super(Util, self).__init__(bigip)
self._meta_data['allowed_lazy_attributes'] = [
Bash,
Unix_Ls,
Unix_Mv,
Unix_Rm
Expand Down
47 changes: 47 additions & 0 deletions f5/bigip/tm/util/test/test_bash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright 2016 F5 Networks Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import mock
import pytest

from f5.bigip import ManagementRoot
from f5.bigip.tm.util.Bash import Bash


@pytest.fixture
def FakeBash():
fake_sys = mock.MagicMock()
fake_bash = Bash(fake_sys)
return fake_bash


@pytest.fixture
def FakeiControl(fakeicontrolsession):
mr = ManagementRoot('host', 'fake_admin', 'fake_admin')
mock_session = mock.MagicMock()
mock_session.post.return_value.json.return_value = {}
mr._meta_data['icr_session'] = mock_session
return mr.tm.util.bash


class TestBashCommand(object):
def test_command_bash(self, FakeiControl):
FakeiControl.exec_cmd('run',
utilCmdArgs='-c "df -k"')
session = FakeiControl._meta_data['bigip']._meta_data['icr_session']
assert session.post.call_args == mock.call(
'https://host:443/mgmt/tm/util/bash/',
json={'utilCmdArgs': '-c "df -k"', 'command': 'run'}
)
66 changes: 66 additions & 0 deletions test/functional/tm/util/test_bash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@

# Copyright 2016 F5 Networks Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import pytest

from f5.bigip.resource import MissingRequiredCommandParameter
from f5.utils.util_exceptions import UtilError
from icontrol.session import iControlUnexpectedHTTPError


def test_E_bash(mgmt_root):

with pytest.raises(MissingRequiredCommandParameter) as err:
mgmt_root.tm.util.bash.exec_cmd('run')
assert "Missing required params: ['utilCmdArgs']" in err.response.text

# use bash to create a test file
bash1 = mgmt_root.tm.util.bash.exec_cmd(
'run',
utilCmdArgs='-c "echo hello >> /var/tmp/test.txt"')

# commandResult should not be present if this was successful
assert 'commandResult' not in bash1.__dict__

bash2 = mgmt_root.tm.util.bash.exec_cmd(
'run',
utilCmdArgs='-c df -k')

# commandResult should b present with data from 'df -k'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"be". Again, we'll fix it in a follow-up PR

assert 'commandResult' in bash2.__dict__

# UtilError should be raised if -c is not specified
with pytest.raises(UtilError) as err:
mgmt_root.tm.util.bash.exec_cmd('run',
utilCmdArgs='-9 hello')
assert 'Required format is "-c <bash command and arguments>"'\
in err.response.text

# UtilError should be raised if command isn't found
with pytest.raises(UtilError) as err:
mgmt_root.tm.util.bash.exec_cmd('run',
utilCmdArgs='-c hello')
assert 'command not found' in err.response.text

# clean up test file
mgmt_root.tm.util.unix_rm.exec_cmd('run', utilCmdArgs='/var/tmp/test.txt')

# iControlUnexpectedHTTPError should be raised if quotes don't match
with pytest.raises(iControlUnexpectedHTTPError) as err:
mgmt_root.tm.util.bash.exec_cmd('run',
utilCmdArgs='-c "df -k')
assert err.response.status_code == 400
assert 'quotes are not balanced' in err.response.text