diff options
-rw-r--r-- | api/base.py | 55 | ||||
-rw-r--r-- | api/resources/__init__.py (renamed from api/actions/__init__.py) | 0 | ||||
-rw-r--r-- | api/resources/env_action.py (renamed from api/actions/env.py) | 0 | ||||
-rw-r--r-- | api/resources/release_action.py (renamed from api/actions/test.py) | 0 | ||||
-rw-r--r-- | api/resources/results.py (renamed from api/actions/result.py) | 4 | ||||
-rw-r--r-- | api/resources/samples_action.py (renamed from api/actions/samples.py) | 0 | ||||
-rw-r--r-- | api/urls.py | 6 | ||||
-rw-r--r-- | api/views.py | 53 | ||||
-rwxr-xr-x | tests/ci/clean_images.sh | 8 | ||||
-rwxr-xr-x | tests/ci/load_images.sh | 47 | ||||
-rwxr-xr-x | tests/ci/prepare_storperf_admin-rc.sh | 5 | ||||
-rw-r--r-- | tests/ci/scp_storperf_admin-rc.sh | 11 | ||||
-rwxr-xr-x | tests/ci/yardstick-verify | 6 | ||||
-rw-r--r-- | tests/opnfv/test_suites/opnfv_components.yaml | 16 | ||||
-rw-r--r-- | tests/opnfv/test_suites/opnfv_features.yaml | 52 | ||||
-rw-r--r-- | tests/opnfv/test_suites/opnfv_performance.yaml | 62 | ||||
-rw-r--r-- | tests/opnfv/test_suites/opnfv_smoke.yaml | 14 | ||||
-rw-r--r-- | tests/unit/test_ssh.py | 36 | ||||
-rwxr-xr-x | tools/yardstick-img-modify | 8 | ||||
-rw-r--r-- | yardstick/ssh.py | 16 |
20 files changed, 310 insertions, 89 deletions
diff --git a/api/base.py b/api/base.py new file mode 100644 index 000000000..7671527d4 --- /dev/null +++ b/api/base.py @@ -0,0 +1,55 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import re +import importlib +import logging + +from flask import request +from flask_restful import Resource + +from api.utils import common as common_utils + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + + +class ApiResource(Resource): + + def _post_args(self): + params = common_utils.translate_to_str(request.json) + action = params.get('action', '') + args = params.get('args', {}) + logger.debug('Input args is: action: %s, args: %s', action, args) + + return action, args + + def _get_args(self): + args = common_utils.translate_to_str(request.args) + logger.debug('Input args is: args: %s', args) + + return args + + def _dispatch_post(self): + action, args = self._post_args() + return self._dispatch(args, action) + + def _dispatch_get(self): + args = self._get_args() + return self._dispatch(args) + + def _dispatch(self, args, action='default'): + module_name = re.sub(r'([A-Z][a-z]*)', r'_\1', + self.__class__.__name__)[1:].lower() + + module_name = 'api.resources.%s' % module_name + resources = importlib.import_module(module_name) + try: + return getattr(resources, action)(args) + except NameError: + common_utils.error_handler('Wrong action') diff --git a/api/actions/__init__.py b/api/resources/__init__.py index e69de29bb..e69de29bb 100644 --- a/api/actions/__init__.py +++ b/api/resources/__init__.py diff --git a/api/actions/env.py b/api/resources/env_action.py index fa0f95d90..fa0f95d90 100644 --- a/api/actions/env.py +++ b/api/resources/env_action.py diff --git a/api/actions/test.py b/api/resources/release_action.py index fda0ffd32..fda0ffd32 100644 --- a/api/actions/test.py +++ b/api/resources/release_action.py diff --git a/api/actions/result.py b/api/resources/results.py index 1f200fbcc..3de09fdc9 100644 --- a/api/actions/result.py +++ b/api/resources/results.py @@ -17,6 +17,10 @@ from api import conf logger = logging.getLogger(__name__) +def default(args): + return getResult(args) + + def getResult(args): try: measurement = args['measurement'] diff --git a/api/actions/samples.py b/api/resources/samples_action.py index 545447aec..545447aec 100644 --- a/api/actions/samples.py +++ b/api/resources/samples_action.py diff --git a/api/urls.py b/api/urls.py index 50be91ead..0fffd12db 100644 --- a/api/urls.py +++ b/api/urls.py @@ -11,8 +11,8 @@ from api.utils.common import Url urlpatterns = [ - Url('/yardstick/testcases/release/action', views.Release, 'release'), - Url('/yardstick/testcases/samples/action', views.Samples, 'samples'), + Url('/yardstick/testcases/release/action', views.ReleaseAction, 'release'), + Url('/yardstick/testcases/samples/action', views.SamplesAction, 'samples'), Url('/yardstick/results', views.Results, 'results'), - Url('/yardstick/env/action', views.Env, 'env') + Url('/yardstick/env/action', views.EnvAction, 'env') ] diff --git a/api/views.py b/api/views.py index 928d8e9eb..ee13b47a9 100644 --- a/api/views.py +++ b/api/views.py @@ -9,18 +9,13 @@ import logging import os -from flask import request -from flask_restful import Resource from flasgger.utils import swag_from -from api.utils import common as common_utils +from api.base import ApiResource from api.swagger import models -from api.actions import test as test_action -from api.actions import samples as samples_action -from api.actions import result as result_action -from api.actions import env as env_action logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) TestCaseActionModel = models.TestCaseActionModel @@ -29,54 +24,26 @@ TestCaseActionArgsOptsModel = models.TestCaseActionArgsOptsModel TestCaseActionArgsOptsTaskArgModel = models.TestCaseActionArgsOptsTaskArgModel -class Release(Resource): +class ReleaseAction(ApiResource): @swag_from(os.getcwd() + '/swagger/docs/testcases.yaml') def post(self): - action = common_utils.translate_to_str(request.json.get('action', '')) - args = common_utils.translate_to_str(request.json.get('args', {})) - logger.debug('Input args is: action: %s, args: %s', action, args) + return self._dispatch_post() - try: - return getattr(test_action, action)(args) - except AttributeError: - return common_utils.error_handler('Wrong action') - -class Samples(Resource): +class SamplesAction(ApiResource): def post(self): - action = common_utils.translate_to_str(request.json.get('action', '')) - args = common_utils.translate_to_str(request.json.get('args', {})) - logger.debug('Input args is: action: %s, args: %s', action, args) - - try: - return getattr(samples_action, action)(args) - except AttributeError: - return common_utils.error_handler('Wrong action') + return self._dispatch_post() ResultModel = models.ResultModel -class Results(Resource): +class Results(ApiResource): @swag_from(os.getcwd() + '/swagger/docs/results.yaml') def get(self): - args = common_utils.translate_to_str(request.args) - action = args.get('action', '') - logger.debug('Input args is: action: %s, args: %s', action, args) + return self._dispatch_get() - try: - return getattr(result_action, action)(args) - except AttributeError: - return common_utils.error_handler('Wrong action') - -class Env(Resource): +class EnvAction(ApiResource): def post(self): - action = common_utils.translate_to_str(request.json.get('action', '')) - args = common_utils.translate_to_str(request.json.get('args', {})) - logger.debug('Input args is: action: %s, args: %s', action, args) - - try: - return getattr(env_action, action)(args) - except AttributeError: - return common_utils.error_handler('Wrong action') + return self._dispatch_post() diff --git a/tests/ci/clean_images.sh b/tests/ci/clean_images.sh index b1942160b..5d661283d 100755 --- a/tests/ci/clean_images.sh +++ b/tests/ci/clean_images.sh @@ -15,15 +15,15 @@ cleanup() echo echo "========== Cleanup ==========" - if ! glance image-list; then + if ! openstack image list; then return fi - for image in $(glance image-list | grep -e cirros-0.3.3 -e yardstick-image -e Ubuntu-14.04 \ + for image in $(openstack image list | grep -e cirros-0.3.3 -e yardstick-image -e Ubuntu-14.04 \ -e yardstick-vivid-kernel | awk '{print $2}'); do echo "Deleting image $image..." - glance image-delete $image || true + openstack image delete $image || true done - nova flavor-delete yardstick-flavor &> /dev/null || true + openstack flavor delete yardstick-flavor &> /dev/null || true } diff --git a/tests/ci/load_images.sh b/tests/ci/load_images.sh index 405d72076..e1d717749 100755 --- a/tests/ci/load_images.sh +++ b/tests/ci/load_images.sh @@ -75,11 +75,12 @@ load_yardstick_image() if [ ! -f $VIVID_KERNEL ]; then tar zxf $VIVID_IMAGE $(basename $VIVID_KERNEL) fi - create_vivid_kernel=$(glance --os-image-api-version 1 image-create \ - --name yardstick-vivid-kernel \ - --is-public true --disk-format qcow2 \ + create_vivid_kernel=$(openstack image create \ + --public \ + --disk-format qcow2 \ --container-format bare \ - --file $VIVID_KERNEL) + --file $VIVID_KERNEL \ + yardstick-vivid-kernel) GLANCE_KERNEL_ID=$(echo "$create_vivid_kernel" | grep " id " | awk '{print $(NF-1)}') if [ -z "$GLANCE_KERNEL_ID" ]; then @@ -101,19 +102,21 @@ load_yardstick_image() fi if [[ "$DEPLOY_SCENARIO" == *"-lxd-"* ]]; then - output=$(eval glance --os-image-api-version 1 image-create \ - --name yardstick-image \ - --is-public true --disk-format root-tar \ + output=$(eval openstack image create \ + --public \ + --disk-format root-tar \ --container-format bare \ $EXTRA_PARAMS \ - --file $RAW_IMAGE) + --file $RAW_IMAGE \ + yardstick-image) else - output=$(eval glance --os-image-api-version 1 image-create \ - --name yardstick-image \ - --is-public true --disk-format qcow2 \ + output=$(eval openstack image create \ + --public \ + --disk-format qcow2 \ --container-format bare \ $EXTRA_PARAMS \ - --file $QCOW_IMAGE) + --file $QCOW_IMAGE \ + yardstick-image) fi echo "$output" @@ -147,12 +150,12 @@ load_cirros_image() EXTRA_PARAMS=$EXTRA_PARAMS" --property hw_mem_page_size=large" fi - output=$(glance image-create \ - --name cirros-0.3.3 \ + output=$(openstack image create \ --disk-format qcow2 \ --container-format bare \ $EXTRA_PARAMS \ - --file $image_file) + --file $image_file \ + cirros-0.3.3) echo "$output" CIRROS_IMAGE_ID=$(echo "$output" | grep " id " | awk '{print $(NF-1)}') @@ -177,12 +180,12 @@ load_ubuntu_image() EXTRA_PARAMS=$EXTRA_PARAMS" --property hw_mem_page_size=large" fi - output=$(glance image-create \ - --name Ubuntu-14.04 \ + output=$(openstack image create \ --disk-format qcow2 \ --container-format bare \ $EXTRA_PARAMS \ - --file $ubuntu_image_file) + --file $ubuntu_image_file \ + Ubuntu-14.04) echo "$output" UBUNTU_IMAGE_ID=$(echo "$output" | grep " id " | awk '{print $(NF-1)}') @@ -197,18 +200,18 @@ load_ubuntu_image() create_nova_flavor() { - if ! nova flavor-list | grep -q yardstick-flavor; then + if ! openstack flavor list | grep -q yardstick-flavor; then echo echo "========== Create nova flavor ==========" # Create the nova flavor used by some sample test cases - nova flavor-create yardstick-flavor 100 512 3 1 + openstack flavor create --id 100 --ram 512 --disk 3 --vcpus 1 yardstick-flavor # DPDK-enabled OVS requires guest memory to be backed by large pages if [[ "$DEPLOY_SCENARIO" == *"-ovs-"* ]]; then - nova flavor-key yardstick-flavor set hw:mem_page_size=large + openstack flavor set --property hw:mem_page_size=large yardstick-flavor fi # VPP requires guest memory to be backed by large pages if [[ "$DEPLOY_SCENARIO" == *"-fdio-"* ]]; then - nova flavor-key yardstick-flavor set hw:mem_page_size=large + openstack flavor set --property hw:mem_page_size=large yardstick-flavor fi fi } diff --git a/tests/ci/prepare_storperf_admin-rc.sh b/tests/ci/prepare_storperf_admin-rc.sh index 0401719ff..b3dc2e58e 100755 --- a/tests/ci/prepare_storperf_admin-rc.sh +++ b/tests/ci/prepare_storperf_admin-rc.sh @@ -9,14 +9,15 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## +# Prepare storperf_admin-rc for StorPerf. + AUTH_URL=${OS_AUTH_URL} USERNAME=${OS_USERNAME:-admin} PASSWORD=${OS_PASSWORD:-console} TENANT_NAME=${OS_TENANT_NAME:-admin} VOLUME_API_VERSION=${OS_VOLUME_API_VERSION:-2} PROJECT_NAME=${OS_PROJECT_NAME:-$TENANT_NAME} -TENANT_ID=`keystone tenant-get admin|grep 'id'|awk -F '|' '{print $3}'|sed -e 's/^[[:space:]]*//'` - +TENANT_ID=`openstack project show admin|grep '\bid\b' |awk -F '|' '{print $3}'|sed -e 's/^[[:space:]]*//'` rm -f ~/storperf_admin-rc touch ~/storperf_admin-rc diff --git a/tests/ci/scp_storperf_admin-rc.sh b/tests/ci/scp_storperf_admin-rc.sh index af2885b01..7c3896d88 100644 --- a/tests/ci/scp_storperf_admin-rc.sh +++ b/tests/ci/scp_storperf_admin-rc.sh @@ -1,5 +1,16 @@ #!/bin/bash +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Copy storperf_admin-rc to deployment location. + ssh_options="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" sshpass -p root scp 2>/dev/null $ssh_options ~/storperf_admin-rc \ root@192.168.200.1:/root/ &> /dev/null diff --git a/tests/ci/yardstick-verify b/tests/ci/yardstick-verify index 7644c96c4..46b32cc2c 100755 --- a/tests/ci/yardstick-verify +++ b/tests/ci/yardstick-verify @@ -301,8 +301,8 @@ main() # check OpenStack services echo "Checking OpenStack services:" - for cmd in "glance image-list" "nova list" "heat stack-list"; do - echo " checking ${cmd/%\ */} ..." + for cmd in "openstack image list" "openstack server list" "openstack stack list"; do + echo " checking ${cmd} ..." if ! $cmd >/dev/null; then echo "error: command \"$cmd\" failed" exit 1 @@ -311,7 +311,7 @@ main() echo echo "Checking for External network:" - for net in $(neutron net-list --router:external True -c name -f value); do + for net in $(openstack network list --external -c Name -f value); do echo " external network: $net" done diff --git a/tests/opnfv/test_suites/opnfv_components.yaml b/tests/opnfv/test_suites/opnfv_components.yaml new file mode 100644 index 000000000..ff4923e03 --- /dev/null +++ b/tests/opnfv/test_suites/opnfv_components.yaml @@ -0,0 +1,16 @@ +--- +# Yardstick components task suite + +schema: "yardstick:suite:0.1" + +name: "opnfv_yardstick-components" +test_cases_dir: "tests/opnfv/test_cases/" +test_cases: +- + file_name: opnfv_yardstick_tc074.yaml + constraint: + installer: compass + pod: huawei-pod1 + task_args: + huawei-pod1: '{"public_network": "ext-net", + "StorPerf_ip": "192.168.200.1"}' diff --git a/tests/opnfv/test_suites/opnfv_features.yaml b/tests/opnfv/test_suites/opnfv_features.yaml new file mode 100644 index 000000000..3621f1367 --- /dev/null +++ b/tests/opnfv/test_suites/opnfv_features.yaml @@ -0,0 +1,52 @@ +--- +# Yardstick features task suite + +schema: "yardstick:suite:0.1" + +name: "opnfv_yardstick-features" +test_cases_dir: "tests/opnfv/test_cases/" +test_cases: +- + file_name: opnfv_yardstick_tc027.yaml + constraint: + installer: compass,fuel + pod: huawei-pod1,lf-pod2 + task_args: + huawei-pod1: '{"pod_info": "etc/yardstick/nodes/compass_sclab_physical/pod.yaml"}' + lf-pod2: '{"pod_info": "etc/yardstick/nodes/fuel_baremetal/pod.yaml", "openrc":"/root/openrc", "external_network":"admin_floating_net"}' +- + file_name: opnfv_yardstick_tc045.yaml + constraint: + installer: fuel +- + file_name: opnfv_yardstick_tc046.yaml + constraint: + installer: fuel +- + file_name: opnfv_yardstick_tc047.yaml + constraint: + installer: fuel +- + file_name: opnfv_yardstick_tc048.yaml + constraint: + installer: fuel +- + file_name: opnfv_yardstick_tc049.yaml + constraint: + installer: fuel +- + file_name: opnfv_yardstick_tc050.yaml + constraint: + installer: fuel +- + file_name: opnfv_yardstick_tc051.yaml + constraint: + installer: fuel +- + file_name: opnfv_yardstick_tc052.yaml + constraint: + installer: fuel +- + file_name: opnfv_yardstick_tc053.yaml + constraint: + installer: fuel diff --git a/tests/opnfv/test_suites/opnfv_performance.yaml b/tests/opnfv/test_suites/opnfv_performance.yaml new file mode 100644 index 000000000..71b1e2ef9 --- /dev/null +++ b/tests/opnfv/test_suites/opnfv_performance.yaml @@ -0,0 +1,62 @@ +--- +# Yardstick performance task suite + +schema: "yardstick:suite:0.1" + +name: "opnfv_yardstick-performance" +test_cases_dir: "tests/opnfv/test_cases/" +test_cases: +- + file_name: opnfv_yardstick_tc002.yaml +- + file_name: opnfv_yardstick_tc005.yaml +- + file_name: opnfv_yardstick_tc010.yaml +- + file_name: opnfv_yardstick_tc011.yaml +- + file_name: opnfv_yardstick_tc012.yaml +- + file_name: opnfv_yardstick_tc014.yaml +- + file_name: opnfv_yardstick_tc037.yaml +- + file_name: opnfv_yardstick_tc043.yaml + constraint: + installer: compass + pod: huawei-pod1 + task_args: + huawei-pod1: '{"pod_info": "etc/yardstick/nodes/compass_sclab_physical/pod.yaml", + "host": "node4.LF","target": "node5.LF"}' +- + file_name: opnfv_yardstick_tc055.yaml + constraint: + installer: compass + pod: huawei-pod1 + task_args: + huawei-pod1: '{"pod_info": "etc/yardstick/nodes/compass_sclab_physical/pod.yaml", + "host": "node5.yardstick-TC055"}' +- + file_name: opnfv_yardstick_tc063.yaml + constraint: + installer: compass + pod: huawei-pod1 + task_args: + huawei-pod1: '{"pod_info": "etc/yardstick/nodes/compass_sclab_physical/pod.yaml", + "host": "node5.yardstick-TC063"}' +- + file_name: opnfv_yardstick_tc069.yaml +- + file_name: opnfv_yardstick_tc070.yaml +- + file_name: opnfv_yardstick_tc071.yaml +- + file_name: opnfv_yardstick_tc072.yaml +- + file_name: opnfv_yardstick_tc075.yaml + constraint: + installer: compass + pod: huawei-pod1 + task_args: + huawei-pod1: '{"pod_info": "etc/yardstick/nodes/compass_sclab_physical/pod.yaml", + "host": "node1.LF"}' diff --git a/tests/opnfv/test_suites/opnfv_smoke.yaml b/tests/opnfv/test_suites/opnfv_smoke.yaml new file mode 100644 index 000000000..f773bec87 --- /dev/null +++ b/tests/opnfv/test_suites/opnfv_smoke.yaml @@ -0,0 +1,14 @@ +--- +# Yardstick smoke task suite + +schema: "yardstick:suite:0.1" + +name: "opnfv_yardstick-smoke" +test_cases_dir: "tests/opnfv/test_cases/" +test_cases: +- + file_name: opnfv_yardstick_tc002.yaml +- + file_name: opnfv_yardstick_tc005.yaml +- + file_name: opnfv_yardstick_tc012.yaml diff --git a/tests/unit/test_ssh.py b/tests/unit/test_ssh.py index 8b828ed7c..045ac0f1b 100644 --- a/tests/unit/test_ssh.py +++ b/tests/unit/test_ssh.py @@ -310,12 +310,38 @@ class SSHRunTestCase(unittest.TestCase): @mock.patch("yardstick.ssh.open", create=True) def test__put_file_shell(self, mock_open): - self.test_client.run = mock.Mock() - self.test_client._put_file_shell("localfile", "remotefile", 0o42) + with mock.patch.object(self.test_client, "run") as run_mock: + self.test_client._put_file_shell("localfile", "remotefile", 0o42) + run_mock.assert_called_once_with( + 'cat > "remotefile"&& chmod -- 042 "remotefile"', + stdin=mock_open.return_value.__enter__.return_value) - self.test_client.run.assert_called_once_with( - 'cat > remotefile && chmod -- 042 remotefile', - stdin=mock_open.return_value.__enter__.return_value) + @mock.patch("yardstick.ssh.open", create=True) + def test__put_file_shell_space(self, mock_open): + with mock.patch.object(self.test_client, "run") as run_mock: + self.test_client._put_file_shell("localfile", + "filename with space", 0o42) + run_mock.assert_called_once_with( + 'cat > "filename with space"&& chmod -- 042 "filename with ' + 'space"', + stdin=mock_open.return_value.__enter__.return_value) + + @mock.patch("yardstick.ssh.open", create=True) + def test__put_file_shell_tilde(self, mock_open): + with mock.patch.object(self.test_client, "run") as run_mock: + self.test_client._put_file_shell("localfile", "~/remotefile", 0o42) + run_mock.assert_called_once_with( + 'cat > ~/"remotefile"&& chmod -- 042 ~/"remotefile"', + stdin=mock_open.return_value.__enter__.return_value) + + @mock.patch("yardstick.ssh.open", create=True) + def test__put_file_shell_tilde_spaces(self, mock_open): + with mock.patch.object(self.test_client, "run") as run_mock: + self.test_client._put_file_shell("localfile", "~/file with space", + 0o42) + run_mock.assert_called_once_with( + 'cat > ~/"file with space"&& chmod -- 042 ~/"file with space"', + stdin=mock_open.return_value.__enter__.return_value) @mock.patch("yardstick.ssh.os.stat") def test__put_file_sftp(self, mock_stat): diff --git a/tools/yardstick-img-modify b/tools/yardstick-img-modify index 0033383ef..68ce6e223 100755 --- a/tools/yardstick-img-modify +++ b/tools/yardstick-img-modify @@ -152,9 +152,15 @@ cleanup() { mount | grep $mountdir/proc && umount $mountdir/proc mount | grep $mountdir && umount $mountdir mount | grep "/mnt/vivid" && umount "/mnt/vivid" + if [ -f $raw_imgfile ]; then - kpartx -dv $raw_imgfile + #kpartx -dv $raw_imgfile sometimes failed, we should checked it agein. + #if [ -z "$(kpartx -l $raw_imgfile | grep 'loop deleted')" ]; then + # kpartx -dv $raw_imgfile + #fi + kpartx -dv $raw_imgfile || true fi + rm -f $raw_imgfile rm -rf $mountdir } diff --git a/yardstick/ssh.py b/yardstick/ssh.py index 3081001b6..927ca94db 100644 --- a/yardstick/ssh.py +++ b/yardstick/ssh.py @@ -66,6 +66,7 @@ import os import select import socket import time +import re import logging import paramiko @@ -252,7 +253,7 @@ class SSH(object): raise SSHError("Socket error.") exit_status = session.recv_exit_status() - if 0 != exit_status and raise_on_error: + if exit_status != 0 and raise_on_error: fmt = "Command '%(cmd)s' failed with exit_status %(status)d." details = fmt % {"cmd": cmd, "status": exit_status} if stderr_data: @@ -311,17 +312,21 @@ class SSH(object): mode = 0o777 & os.stat(localpath).st_mode sftp.chmod(remotepath, mode) + TILDE_EXPANSIONS_RE = re.compile("(^~[^/]*/)?(.*)") + def _put_file_shell(self, localpath, remotepath, mode=None): # quote to stop wordpslit - cmd = ['cat > %s' % remotepath] + tilde, remotepath = self.TILDE_EXPANSIONS_RE.match(remotepath).groups() + if not tilde: + tilde = '' + cmd = ['cat > %s"%s"' % (tilde, remotepath)] if mode is not None: # use -- so no options - cmd.append('chmod -- 0%o %s' % (mode, remotepath)) + cmd.append('chmod -- 0%o %s"%s"' % (mode, tilde, remotepath)) with open(localpath, "rb") as localfile: # only chmod on successful cat - cmd = " && ".join(cmd) - self.run(cmd, stdin=localfile) + self.run("&& ".join(cmd), stdin=localfile) def put_file(self, localpath, remotepath, mode=None): """Copy specified local file to the server. @@ -330,7 +335,6 @@ class SSH(object): :param remotepath: Remote filename. :param mode: Permissions to set after upload """ - import socket try: self._put_file_sftp(localpath, remotepath, mode=mode) except (paramiko.SSHException, socket.error): |