Merge lp:~canonical-platform-qa/snappy-ecosystem-tests/helpers_for_snapd_cli into lp:snappy-ecosystem-tests

Proposed by Omer Akram
Status: Merged
Approved by: Heber Parrucci
Approved revision: 37
Merged at revision: 23
Proposed branch: lp:~canonical-platform-qa/snappy-ecosystem-tests/helpers_for_snapd_cli
Merge into: lp:snappy-ecosystem-tests
Prerequisite: lp:~canonical-platform-qa/snappy-ecosystem-tests/minor_tweaks
Diff against target: 359 lines (+314/-0)
6 files modified
README.rst (+6/-0)
requirements.txt (+2/-0)
snappy_ecosystem_tests/helpers/snapd/snapd.py (+137/-0)
snappy_ecosystem_tests/tests/test_snapd.py (+42/-0)
snappy_ecosystem_tests/utils/ssh.py (+92/-0)
snappy_ecosystem_tests/utils/user.py (+35/-0)
To merge this branch: bzr merge lp:~canonical-platform-qa/snappy-ecosystem-tests/helpers_for_snapd_cli
Reviewer Review Type Date Requested Status
Heber Parrucci (community) Approve
Santiago Baldassin (community) Approve
platform-qa-bot continuous-integration Approve
I Ahmad (community) Needs Fixing
Review via email: mp+316459@code.launchpad.net

Commit message

Basic helpers around snapd' command line interface

Description of the change

Basic helpers around snapd' command line interface

To post a comment you must log in.
Revision history for this message
I Ahmad (iahmad) wrote :

Please see my inline comments

review: Needs Fixing
Revision history for this message
I Ahmad (iahmad) :
8. By Omer Akram

merge with trunk

9. By Omer Akram

Assert when logged in

Revision history for this message
Omer Akram (om26er) wrote :

Replied inline.

10. By Omer Akram

fix login command

Revision history for this message
I Ahmad (iahmad) wrote :
Download full text (7.7 KiB)

I am fine as long as data is not parsed/filtered within helper method

On Tue, Feb 7, 2017 at 7:48 PM, Omer Akram <email address hidden> wrote:

> Replied inline.
>
> Diff comments:
>
> >
> > === modified file 'tests/utils/snapd.py'
> > --- tests/utils/snapd.py 2017-02-06 14:05:03 +0000
> > +++ tests/utils/snapd.py 2017-02-06 14:05:03 +0000
> > @@ -17,3 +17,138 @@
> > # You should have received a copy of the GNU General Public License
> > # along with this program. If not, see <http://www.gnu.org/licenses/>.
> > #
> > +
> > +import getpass
> > +import os
> > +import subprocess
> > +import sys
> > +import shlex
> > +import shutil
> > +import tempfile
> > +
> > +import pexpect
> > +import yaml
> > +
> > +PATH_SNAP = '/usr/bin/snap'
> > +COMMAND_DOWNLOAD = 'download {snap} --channel={channel}'
> > +COMMAND_FIND = 'find {search_term}'
> > +COMMAND_INFO = 'info {snap}'
> > +COMMAND_INSTALL = 'install {snap} --channel={channel}'
> > +COMMAND_LOGIN = 'login {email}'
> > +COMMAND_LOGOUT = 'logout'
> > +COMMAND_REFRESH = 'refresh {snap} --channel={channel}'
> > +CHANNEL_STABLE = 'stable'
> > +PASSWORD_ROOT = 'changed'
> > +
> > +
> > +def is_root_user():
> > + """Return bool representing if the current user is root."""
> > + return os.getuid() == 0
> > +
> > +
> > +def run_snapd_command(parameters, return_output=False, cwd=None):
> > + """ Run the request snapd cli command.
> > +
> > + :param parameters: the snapd sub-command and their parameters.
> > + example: install core --beta
> > + :param return_output: Whether to return the stdout of the command.
> > + :param cwd: The current working directory for the command, defaults
> to
> > + pwd.
> > + :return: Optionally return the stdout of the command, depending if
> > + the value of `return_output` was True.
> > + """
> > + command = '{} {}'.format(PATH_SNAP, parameters)
> > + if return_output:
> > + raw_output = subprocess.check_output(
> > + shlex.split(command), universal_newlines=True, cwd=cwd)
> > + return raw_output.strip().split('\n')
> > + subprocess.check_call(shlex.split(command), cwd=cwd)
> > +
> > +
> > +def login(email, password):
> > + """Login to snapd.
> > +
> > + :param email: Ubuntu SSO account email address.
> > + :param password: Ubuntu SSO account password.
> > + """
> > + command = '{} {}'.format(COMMAND_LOGIN, email)
> > + if is_root_user():
> > + p = pexpect.spawnu(command, logfile=sys.stdout)
> > + else:
> > + p = pexpect.spawnu('{} {}'.format('sudo', command),
> logfile=sys.stdout)
> > + p.expect_exact(
> > + '[sudo] password for {}: '.format(getpass.getuser()))
> > + p.sendline(PASSWORD_ROOT)
> > + p.expect_exact('Password of "{}": '.format(email))
> > + p.sendline(password)
> > + p.wait()
> > +
>
> I added an assert to ensure the login was successful.
>
> > +
> > +def logout():
> > + run_snapd_command(COMMAND_LOGOUT)
> > +
> > +
> > +def download(snap, channel=CHANNEL_STABLE, path=None):
> > + """Download the requested snap.
> > +
> > + :param snap: name of the snap to download.
> > + :param channel:...

Read more...

11. By Omer Akram

Simplify .
./snapd.py
./snapcraft.py
./__pycache__
./__pycache__/snapd.cpython-35.pyc
./__init__.py method to remove client side filtering

Revision history for this message
Omer Akram (om26er) wrote :

Ok, removed the filtering code now.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
12. By Omer Akram

Merge with trunk

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Omer Akram (om26er) wrote :

I have made the suggested changes.

13. By Omer Akram

Fix pylint complaints

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
14. By Omer Akram

add fixme comment

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
15. By Omer Akram

more compact code. always return command output

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :

Looks good in general. Couple of comments inline. Additionally:

* Merge from trunk
* Can you add a single test that checks if it is possible to login in snapd? in /tests folder
* Can you move snapd.py from utils to helpers?

review: Needs Fixing
16. By Omer Akram

revert

17. By Omer Akram

merge with trunk

18. By Omer Akram

bring back changes

19. By Omer Akram

read password from config, other changes

20. By Omer Akram

add simple test case for snapd

Revision history for this message
Omer Akram (om26er) wrote :

Replied inline also added a few simple test cases.

Revision history for this message
Heber Parrucci (heber013) wrote :

Looks good. One comment inline

review: Needs Fixing
Revision history for this message
Omer Akram (om26er) wrote :

sure, let me fix that.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
21. By Omer Akram

Update snapd helpers to execute over ssh

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
22. By Omer Akram

Merge with trunk

23. By Omer Akram

pylint happy

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Omer Akram (om26er) wrote :

I updated this branch to execute all commands over ssh. With paramiko we automagically get a persistent ssh connection, so the performance should not be a problem. The remote machine should have `expect` installed. Both snapd and snapcraft commands should work just fine over ssh. For store api calls, we need a small wrapper that builds curl commands based on our requirements to be run over ssh.

with that we can have a single way to run tests on all targets (kvm, bare metal, all-snap(in future), lxd or a tablet.)

TODO: run_command_over_ssh need to be changed to actually utilize connection persistence advantages of paramiko, but I wanted to check how we all think about this direction.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :

Looks good. Just minor comments

review: Needs Fixing
24. By Omer Akram

Maintain persistent ssh object, fix review comments

25. By Omer Akram

rename and move ssh runner function to ssh.py

Revision history for this message
Omer Akram (om26er) wrote :

Replied inline.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
26. By Omer Akram

More readable calls

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
27. By Omer Akram

fix some words

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
28. By Omer Akram

ignore chrome driver from requirements

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

Looks good but I think the ssh approach does not entirely solve the issue we discussed. See inline

review: Needs Fixing
Revision history for this message
Omer Akram (om26er) wrote :

Added a comment.

Revision history for this message
Omer Akram (om26er) wrote :

Adding slightly less relevant comment.

29. By Omer Akram

Add protection against direct initialization of SSHClient

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
30. By Omer Akram

Merge with trunk

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :

See comments inline.

review: Needs Fixing
31. By Omer Akram

Add optional overriding of hostname and username to run commands

Revision history for this message
Omer Akram (om26er) wrote :

Replied inline, also extended run_command to optionally take hostname and username as suggested in your code.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

Reply inline. Let's talk about this over the phone before we get into an endless discussion

review: Needs Fixing
Revision history for this message
Omer Akram (om26er) wrote :

Replied and lets chat.

Revision history for this message
Heber Parrucci (heber013) wrote :

Reply inline. Let's talk offline

Revision history for this message
Sergio Cazzolato (sergio-j-cazzolato) wrote :

Comment inline

32. By Omer Akram

Update to a SSHManager approach to keep a pool of active connections

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Omer Akram (om26er) wrote :

Replied inline.

33. By Omer Akram

Add doc-string

Revision history for this message
Omer Akram (om26er) wrote :

Sergio, replied inline.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
34. By Omer Akram

More simpler and compact approach

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
35. By Omer Akram

reuse some code

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :

Looks much better. Just a question inline.
Additionally, can you update README with remote host credentials info? you can reuse User Credentials section in that file if you wish.

36. By Omer Akram

update README for ssh credentials

Revision history for this message
Omer Akram (om26er) wrote :

Updated README, added inline comments.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

Looks good. Minor comments inline

Revision history for this message
Heber Parrucci (heber013) wrote :

Reply to Santiago's suggestion

Revision history for this message
Omer Akram (om26er) wrote :

Replied.

Revision history for this message
Omer Akram (om26er) wrote :

re.

37. By Omer Akram

Changes

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

Looks Good. Thanks

review: Approve
Revision history for this message
Heber Parrucci (heber013) wrote :

Code LGTM. Let's land it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.rst'
2--- README.rst 2017-02-20 22:56:17 +0000
3+++ README.rst 2017-02-22 19:08:43 +0000
4@@ -69,10 +69,16 @@
5 [user]
6 user_email=^USER_NAME^
7 user_password=^USER_PASSWORD^
8+hostname_remote=^SSH_HOSTNAME^
9+username_remote=^SSH_USERNAME^
10+port_remote=^SSH_PORT^
11
12 option 2 - Set the following environment variables:
13 user_email=^USER_NAME^
14 user_password=^USER_PASSWORD^
15+hostname_remote=^SSH_HOSTNAME^
16+username_remote=^SSH_USERNAME^
17+port_remote=^SSH_PORT^
18
19
20 Changing store:
21
22=== modified file 'requirements.txt'
23--- requirements.txt 2017-02-21 12:00:19 +0000
24+++ requirements.txt 2017-02-22 19:08:43 +0000
25@@ -16,3 +16,5 @@
26 requests-toolbelt==0.6.0
27 #chromedriver_installer
28 pylxd
29+pyyaml
30+paramiko
31
32=== modified file 'snappy_ecosystem_tests/helpers/snapd/snapd.py'
33--- snappy_ecosystem_tests/helpers/snapd/snapd.py 2017-02-09 20:09:40 +0000
34+++ snappy_ecosystem_tests/helpers/snapd/snapd.py 2017-02-22 19:08:43 +0000
35@@ -17,3 +17,140 @@
36 # You should have received a copy of the GNU General Public License
37 # along with this program. If not, see <http://www.gnu.org/licenses/>.
38 #
39+
40+"""Helpers around snapd command-line interface."""
41+
42+import json
43+import logging
44+
45+import yaml
46+
47+from snappy_ecosystem_tests.utils import ssh
48+
49+PATH_SNAP = '/usr/bin/snap'
50+COMMAND_DOWNLOAD = 'download {snap} --channel={channel}'
51+COMMAND_FIND = 'find {search_term}'
52+COMMAND_INFO = 'info {snap}'
53+COMMAND_INSTALL = 'install {snap} --channel={channel}'
54+COMMAND_LIST = 'list'
55+COMMAND_LOGIN = 'login {email}'
56+COMMAND_LOGOUT = 'logout'
57+COMMAND_REFRESH = 'refresh {snap} --channel={channel}'
58+COMMAND_REMOVE = 'remove {snap}'
59+CHANNEL_STABLE = 'stable'
60+COMMANDS_LOGIN = """\
61+/usr/bin/expect \
62+-c 'spawn snap login {email}' \
63+-c 'expect \"Password*\"' \
64+-c 'send {password}\\r' \
65+-c 'interact'\
66+"""
67+LOGGER = logging.getLogger(__name__)
68+
69+
70+def run_snapd_command_ssh(parameters, cwd=''):
71+ """Run snapd command over ssh.
72+
73+ :param parameters: a string containing parameters for the `snap` command.
74+ :param cwd: the current working directory on the remote where the command
75+ should run.
76+ :returns: stdout of the command
77+ """
78+ if cwd:
79+ return ssh.run_command(
80+ 'cd {}; {} {}'.format(cwd, PATH_SNAP, parameters))
81+ return ssh.run_command('{} {}'.format(PATH_SNAP, parameters))
82+
83+
84+def login(email, password):
85+ """Login to snapd.
86+
87+ :param email: Ubuntu SSO account email address.
88+ :param password: Ubuntu SSO account password.
89+ """
90+ ssh.run_command(COMMANDS_LOGIN.format(email=email, password=password))
91+ return is_logged_in(email)
92+
93+
94+def is_logged_in(email):
95+ """Return bool representing if the user is logged into snapd."""
96+ try:
97+ return json.loads(ssh.run_command('cat ~/.snap/auth.json')).get(
98+ 'email') == email
99+ except ValueError:
100+ return False
101+
102+
103+def logout(email):
104+ """Logout snapd current user."""
105+ if is_logged_in(email):
106+ run_snapd_command_ssh(COMMAND_LOGOUT)
107+
108+
109+def download(snap, channel=CHANNEL_STABLE):
110+ """Download the requested snap.
111+
112+ :param snap: name of the snap to download.
113+ :param channel: name of the release channel to download from.
114+ """
115+ command = COMMAND_DOWNLOAD.format(snap=snap, channel=channel)
116+ run_snapd_command_ssh(command, cwd=ssh.run_command('mktemp -d'))
117+
118+
119+def install(snap, channel=CHANNEL_STABLE):
120+ """Install the requested snap."""
121+ run_snapd_command_ssh(COMMAND_INSTALL.format(snap=snap, channel=channel))
122+
123+
124+def _is_installed(snap):
125+ """Return bool representing whether a snap is installed."""
126+ for installed_snap in _parse_output(run_snapd_command_ssh(COMMAND_LIST)):
127+ if installed_snap['name'] == snap:
128+ return True
129+ return False
130+
131+
132+def remove(snap):
133+ """Remove a snap, if its already installed."""
134+ if _is_installed(snap):
135+ run_snapd_command_ssh(COMMAND_REMOVE.format(snap=snap))
136+
137+
138+def info(snap, verbose=False):
139+ """Query the Ubuntu store of the information about a snap.
140+
141+ :param snap: Name of the snap for which the info is required.
142+ :param verbose: Whether to information should be detailed.
143+ :return: Return a dictionary containing information about the snap.
144+ """
145+ command = COMMAND_INFO.format(snap=snap)
146+ if verbose:
147+ command = ' '.join([command, '--verbose'])
148+ return yaml.load("""{}""".format(run_snapd_command_ssh(command)))
149+
150+
151+def refresh(snap, channel=CHANNEL_STABLE):
152+ """Refresh the requested snap."""
153+ run_snapd_command_ssh(COMMAND_REFRESH.format(snap=snap, channel=channel))
154+
155+
156+def _parse_output(raw_output):
157+ """Pretty parse the output from snapd commands like `find` and `list`.
158+
159+ :param raw_output: The raw output returned from the command
160+ :return: A list of dictionaries containing sorted results from the output.
161+ """
162+ split_output = raw_output.split('\n')
163+ headers = [header.lower() for header in split_output.pop(0).split()]
164+ return [dict(zip(headers, line.split())) for line in split_output]
165+
166+
167+def find(keyword):
168+ """Find snaps based on the provided filters
169+
170+ :param keyword: Keyword to use for the query.
171+ :return: Return a list of dictionaries containing information about
172+ snaps matching the `keyword`.
173+ """
174+ return _parse_output(
175+ run_snapd_command_ssh(COMMAND_FIND.format(search_term=keyword)))
176
177=== added file 'snappy_ecosystem_tests/tests/test_snapd.py'
178--- snappy_ecosystem_tests/tests/test_snapd.py 1970-01-01 00:00:00 +0000
179+++ snappy_ecosystem_tests/tests/test_snapd.py 2017-02-22 19:08:43 +0000
180@@ -0,0 +1,42 @@
181+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
182+
183+#
184+# Snappy Ecosystem Tests
185+# Copyright (C) 2017 Canonical
186+#
187+# This program is free software: you can redistribute it and/or modify
188+# it under the terms of the GNU General Public License as published by
189+# the Free Software Foundation, either version 3 of the License, or
190+# (at your option) any later version.
191+#
192+# This program is distributed in the hope that it will be useful,
193+# but WITHOUT ANY WARRANTY; without even the implied warranty of
194+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
195+# GNU General Public License for more details.
196+#
197+# You should have received a copy of the GNU General Public License
198+# along with this program. If not, see <http://www.gnu.org/licenses/>.
199+#
200+
201+"""Snapd testcases."""
202+
203+import testtools
204+
205+from snappy_ecosystem_tests.helpers.snapd import snapd
206+from snappy_ecosystem_tests.utils.storeconfig import get_store_credentials
207+
208+
209+class SnapdTestCase(testtools.TestCase):
210+ """Tests for snapd."""
211+ def setUp(self):
212+ super().setUp()
213+
214+ def test_info(self):
215+ """Ensure the publisher of core snap in canonical."""
216+ self.assertTrue('canonical', snapd.info('core')['publisher'])
217+
218+ def test_login(self):
219+ """Login the snapd user."""
220+ email, password = get_store_credentials()
221+ self.assertTrue(snapd.login(email, password))
222+ self.addCleanup(snapd.logout, email)
223
224=== added file 'snappy_ecosystem_tests/utils/ssh.py'
225--- snappy_ecosystem_tests/utils/ssh.py 1970-01-01 00:00:00 +0000
226+++ snappy_ecosystem_tests/utils/ssh.py 2017-02-22 19:08:43 +0000
227@@ -0,0 +1,92 @@
228+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
229+
230+#
231+# Snappy Ecosystem Tests
232+# Copyright (C) 2017 Canonical
233+#
234+# This program is free software: you can redistribute it and/or modify
235+# it under the terms of the GNU General Public License as published by
236+# the Free Software Foundation, either version 3 of the License, or
237+# (at your option) any later version.
238+#
239+# This program is distributed in the hope that it will be useful,
240+# but WITHOUT ANY WARRANTY; without even the implied warranty of
241+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
242+# GNU General Public License for more details.
243+#
244+# You should have received a copy of the GNU General Public License
245+# along with this program. If not, see <http://www.gnu.org/licenses/>.
246+#
247+
248+"""Module to connect to ssh client for running tests."""
249+
250+import paramiko
251+
252+from snappy_ecosystem_tests.utils.user import get_remote_host_credentials
253+
254+
255+class SSHManager:
256+ """Manager class to keep a pool of ssh connections."""
257+ # Mangle the pool so that it cannot be easily touched from outside.
258+ __connection_pool = []
259+
260+ def __init__(self):
261+ raise NotImplementedError(
262+ 'Class cannot be instantiated, use get_instance() to get a '
263+ 'SSHClient instance.')
264+
265+ @staticmethod
266+ def get_instance(hostname, username, port=22, **kwargs):
267+ """Return the instance of SSHClient from a pool of active connections,
268+ otherwise create a new connection and return."""
269+ port = int(port)
270+ client = SSHManager.get_client(hostname, username, port)
271+ if client:
272+ if not client.get_transport().is_active():
273+ client.connect(
274+ hostname, username=username, port=port, **kwargs)
275+ else:
276+ client = paramiko.SSHClient()
277+ client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
278+ client.connect(hostname, username=username, port=port, **kwargs)
279+ SSHManager.__connection_pool.append(client)
280+ return client
281+
282+ @staticmethod
283+ def sync():
284+ """Update the connection pool to only keep alive connections."""
285+ pool_copy = SSHManager.__connection_pool.copy()
286+ for client in SSHManager.__connection_pool:
287+ if not client.get_transport().is_alive():
288+ pool_copy.remove(client)
289+ SSHManager.__connection_pool = pool_copy
290+
291+ @staticmethod
292+ def get_client(hostname, username, port):
293+ """Return the matching client from the pool based on provided
294+ constraints."""
295+ connections = [
296+ client for client in SSHManager.__connection_pool if
297+ client.get_transport().getpeername() == (hostname, port) and
298+ username == client.get_transport().get_username()
299+ ]
300+ return connections[0] if connections else None
301+
302+
303+def run_command(command, hostname=None, username=None, port=None):
304+ """Run the given command on remote machine over ssh.
305+
306+ :param command: a string of the command to run.
307+ :param hostname: The host to run command on.
308+ :param username: Name of the user on the remote host to login to.
309+ :param port: SSH port number.
310+ :raises ValueError: if command exits with non-zero status.
311+ :return: the stdout of the command.
312+ """
313+ _hostname, _username, _port = get_remote_host_credentials()
314+ ssh = SSHManager.get_instance(
315+ hostname or _hostname, username or _username, port or _port)
316+ _, stdout, stderr = ssh.exec_command(command)
317+ if stdout.channel.recv_exit_status() != 0:
318+ raise ValueError(stderr.read().decode())
319+ return stdout.read().decode()
320
321=== added file 'snappy_ecosystem_tests/utils/user.py'
322--- snappy_ecosystem_tests/utils/user.py 1970-01-01 00:00:00 +0000
323+++ snappy_ecosystem_tests/utils/user.py 2017-02-22 19:08:43 +0000
324@@ -0,0 +1,35 @@
325+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
326+
327+#
328+# Snappy Ecosystem Tests
329+# Copyright (C) 2017 Canonical
330+#
331+# This program is free software: you can redistribute it and/or modify
332+# it under the terms of the GNU General Public License as published by
333+# the Free Software Foundation, either version 3 of the License, or
334+# (at your option) any later version.
335+#
336+# This program is distributed in the hope that it will be useful,
337+# but WITHOUT ANY WARRANTY; without even the implied warranty of
338+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
339+# GNU General Public License for more details.
340+#
341+# You should have received a copy of the GNU General Public License
342+# along with this program. If not, see <http://www.gnu.org/licenses/>.
343+#
344+
345+"""Get host user data."""
346+
347+import os
348+
349+from snappy_ecosystem_tests.commons.config import USER_CONFIG_STACK
350+
351+
352+def get_remote_host_credentials():
353+ """Return credentials for remote machine, to run commands on."""
354+ return (USER_CONFIG_STACK.get('user', 'hostname_remote',
355+ default=os.environ.get('hostname_remote')),
356+ USER_CONFIG_STACK.get('user', 'username_remote',
357+ default=os.environ.get('username_remote')),
358+ USER_CONFIG_STACK.get('user', 'port_remote',
359+ default=os.environ.get('port_remote')))

Subscribers

People subscribed via source and target branches