未验证 提交 54b4ebaa 编写于 作者: C Cleber Rosa

Merge remote-tracking branch 'clebergnu/drop_restclient'

Signed-off-by: NCleber Rosa <crosa@redhat.com>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
def action(function):
"""
Simple function that marks functions as CLI actions
:param function: the function that will receive the CLI action mark
"""
function.is_action = True
return function
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
"""
Module that implements the actions for the CLI App when the job toplevel
command is used
"""
from . import base
from ... import connection
from ....output import LOG_UI
@base.action
def status(app):
"""
Shows the server status
"""
data = app.connection.request("version/")
LOG_UI.info("Server version: %s", data.get('version'))
@base.action
def list_brief(app):
"""
Shows the server API list
"""
try:
data = app.connection.get_api_list()
except connection.UnexpectedHttpStatusCode as detail:
if detail.received == 403:
LOG_UI.error("Error: Access Forbidden")
return False
LOG_UI.info("Available APIs:")
for name in data:
LOG_UI.info(" * %s", name)
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
"""
This is the main entry point for the rest client cli application
"""
import importlib
import sys
import types
from . import parser
from .. import connection
from ... import exit_codes
from ...output import LOG_UI
__all__ = ['App']
class App:
"""
Base class for CLI application
"""
def __init__(self):
"""
Initializes a new app instance.
This class is intended both to be used by the stock client application
and also to be reused by custom applications. If you want, say, to
limit the amount of command line actions and its arguments, you can
simply supply another argument parser class to this constructor. Of
course another way to customize it is to inherit from this and modify
its members at will.
"""
self.connection = None
self.parser = parser.Parser()
self.parser.add_arguments_on_all_modules()
self.log = LOG_UI
def initialize_connection(self):
"""
Initialize the connection instance
"""
try:
self.connection = connection.Connection(
hostname=self.args.hostname,
port=self.args.port,
username=self.args.username,
password=self.args.password)
except connection.InvalidConnectionError:
self.log.error("Error: could not connect to the server")
sys.exit(exit_codes.AVOCADO_FAIL)
except connection.InvalidServerVersionError:
self.log.error("REST server version is higher than "
"than this client can support.")
self.log.error("Please use a more recent version "
"of the REST client application.")
sys.exit(exit_codes.AVOCADO_FAIL)
def dispatch_action(self):
"""
Calls the actions that was specified via command line arguments.
This involves loading the relevant module file.
"""
module_name = "%s.%s" % ('avocado.core.restclient.cli.actions',
self.args.top_level_action)
try:
module = importlib.import_module(module_name)
except ImportError:
return
# Filter out the attributes out of the loaded module that look
# like command line actions, based on type and 'is_action' attribute
module_actions = {}
for attribute_name in module.__dict__:
attribute = module.__dict__[attribute_name]
if (isinstance(attribute, types.FunctionType) and
hasattr(attribute, 'is_action')):
if attribute.is_action:
module_actions[attribute_name] = attribute
chosen_action = None
for action in module_actions.keys():
if getattr(self.args, action, False):
chosen_action = action
break
kallable = module_actions.get(chosen_action, None)
if kallable is not None:
self.initialize_connection()
return kallable(self)
else:
self.log.error("Action specified is not implemented")
def run(self):
"""
Main entry point for application
"""
action_result = None
try:
self.args = self.parser.parse_args()
action_result = self.dispatch_action()
except KeyboardInterrupt:
print('Interrupted')
if isinstance(action_result, int):
sys.exit(action_result)
elif isinstance(action_result, bool):
if action_result is True:
sys.exit(0)
else:
sys.exit(1)
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
"""
This module has base action arguments that are used on other top level commands
These top level commands import these definitions for uniformity and
consistency sake
"""
__all__ = ['ADD', 'LIST_BRIEF', 'LIST_FULL', 'DELETE', 'NAME', 'ID']
#
# Arguments that are treated as actions
#
ADD = (('-a', '--add',),
{'help': 'add a new entry',
'action': 'store_true',
'default': False})
LIST_BRIEF = (('-l', '--list-brief',),
{'help': 'list all records briefly',
'action': 'store_true',
'default': False})
LIST_FULL = (('-L', '--list-full',),
{'help': 'list all records with all information',
'action': 'store_true',
'default': False})
DELETE = (('-d', '--delete',),
{'help': 'delete an existing object',
'action': 'store_true',
'default': False})
#
# Other arguments that will influence action behaviour
#
NAME = (('-n', '--name'),
{'help': 'name of the object'})
ID = (('-i', '--id'),
{'help': 'numeric identification of the object',
'type': int})
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
"""
This module has actions for the server command
"""
from . import base
__all__ = ['ACTION_STATUS', 'ACTION_ARGUMENTS', 'ARGUMENTS']
#
# Arguments that are treated as actions
#
ACTION_STATUS = (('-s', '--status',),
{'help': 'shows the avocado-server status',
'action': 'store_true',
'default': False})
#
# Arguments that are treated as actions
#
ACTION_ARGUMENTS = [base.LIST_BRIEF,
ACTION_STATUS]
#
# Other arguments that will influence action behaviour
#
ARGUMENTS = []
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright (c) 2013-2015 Red Hat
# Author: Cleber Rosa <cleber@redhat.com>
"""
REST client application command line parsing
"""
import os
import glob
import argparse
import importlib
from ...version import VERSION
class Parser(argparse.ArgumentParser):
'''
The main CLI Argument Parser.
'''
def __init__(self, **kwargs):
'''
Initializes a new parser
'''
super(Parser, self).__init__(
description='Avocado Rest Client %s' % VERSION,
**kwargs
)
self._subparsers = None
self._add_global_arguments()
def _add_global_arguments(self):
'''
Add global arguments, that is, do not depend on a specific command
'''
connection_group = self.add_argument_group(
'CONNECTION',
'Set connection options to an Avocado Server')
connection_group.add_argument(
'--hostname',
help='Hostname or IP address for the avocado server',
default='localhost')
connection_group.add_argument(
'--port',
help='Port where avocado server is listening on',
default=9405)
connection_group.add_argument(
'--username',
help='Username to authenticate to avocado server')
connection_group.add_argument(
'--password',
help='Password to give to avocado server')
def add_arguments_on_all_modules(self,
prefix='avocado.core.restclient.cli.args'):
'''
Add arguments that are present on all Python modules at a given prefix
:param prefix: a Python module namespace
'''
blacklist = ('base', '__init__')
basemod = importlib.import_module(prefix)
basemod_dir = os.path.dirname(basemod.__file__)
# FIXME: This works for CPython and IronPython, but not for Jython
mod_files_pattern = os.path.join(basemod_dir, "*.py")
mod_files = glob.glob(mod_files_pattern)
mod_names_with_suffix = [os.path.basename(f) for f in mod_files]
mod_names = [n.replace(".py", "")
for n in mod_names_with_suffix]
mod_names = [n for n in mod_names if n not in blacklist]
for module in mod_names:
self.add_arguments_on_module(module, prefix)
def add_arguments_on_module(self, name, prefix):
'''
Add arguments that are present on a given Python module
:param name: the name of the Python module, without the namespace
'''
if self._subparsers is None:
self._subparsers = self.add_subparsers(
prog='avocado-rest-client',
title='Top Level Command',
dest='top_level_action'
)
module_name = "%s.%s" % (prefix, name)
module = importlib.import_module(module_name)
parser = self._subparsers.add_parser(name)
if hasattr(module, 'ACTION_ARGUMENTS'):
if module.ACTION_ARGUMENTS:
act_grp = parser.add_argument_group("ACTION",
"Action to be performed")
act_excl = act_grp.add_mutually_exclusive_group(required=True)
for action in module.ACTION_ARGUMENTS:
act_excl.add_argument(*action[0], **action[1])
if hasattr(module, 'ARGUMENTS'):
if module.ARGUMENTS:
for arg in module.ARGUMENTS:
# Support either both short+long options or either one, short OR long
short_and_or_long_opts = arg[0]
if len(short_and_or_long_opts) == 1:
parser.add_argument(arg[0][0], **arg[1])
else:
parser.add_argument(*arg[0], **arg[1])
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
"""
This module provides connection classes the avocado server.
A connection is a simple wrapper around a HTTP request instance. It is this
basic object that allows methods to be called on the remote server.
"""
import requests
from ..settings import settings
__all__ = ['get_default', 'Connection']
#: Minimum required version of server side API
MIN_REQUIRED_VERSION = (0, 2, 0)
class InvalidConnectionError(Exception):
"""
Invalid connection for selected server
"""
class InvalidServerVersionError(Exception):
"""
The server version does not satisfy the minimum required version
"""
class UnexpectedHttpStatusCode(Exception):
"""
Server has returned a response with a status code different than expected
"""
def __init__(self, expected, received):
self.expected = expected
self.received = received
def __str__(self):
msg = "Unexpected HTTP response status: expected %s, received %s"
return msg % (self.expected, self.received)
class Connection:
"""
Connection to the avocado server
"""
def __init__(self, hostname=None, port=None, username=None, password=None):
"""
Initializes a connection to an avocado-server instance
:param hostname: the hostname or IP address to connect to
:type hostname: str
:param port: the port number where avocado-server is running
:type port: int
:param username: the name of the user to be authenticated as
:type username: str
:param password: the password to use for authentication
:type password: str
"""
if hostname is None:
hostname = settings.get_value('restclient.connection',
'hostname', default='localhost')
self.hostname = hostname
if port is None:
port = settings.get_value('restclient.connection',
'port', key_type='int',
default=9405)
self.port = port
if username is None:
username = settings.get_value('restclient.connection',
'username', default='')
self.username = username
if password is None:
password = settings.get_value('restclient.connection',
'password', default='')
self.password = password
try:
version = self.request('version')
except (requests.exceptions.ConnectionError, UnexpectedHttpStatusCode):
raise InvalidConnectionError
if not self.check_min_version(version):
raise InvalidServerVersionError
def get_url(self, path=None):
"""
Returns a representation of the current connection as an HTTP URL
"""
if path is None:
return 'http://%s:%s' % (self.hostname, self.port)
return 'http://%s:%s/%s' % (self.hostname, self.port, path)
def request(self, path, method=requests.get, check_status=True, **data):
"""
Performs a request to the server
This method is heavily used by upper level API methods, and more often
than not, those upper level API methods should be used instead.
:param path: the path on the server where the resource lives
:type path: str
:param method: the method you want to call on the remote server,
defaults to a HTTP GET
:param check_status: whether to check the HTTP status code that comes
with the response. If set to `True`, it will
depend on the method chosen. If set to `False`,
no check will be performed. If an integer is given
then that specific status will be checked for.
:param data: keyword arguments to be passed to the remote method
:returns: JSON data
"""
url = self.get_url(path)
if self.username and self.password:
response = method(url,
auth=(self.username, self.password),
params=data)
else:
response = method(url, params=data)
want_status = None
if check_status is True:
if method == requests.get:
want_status = 200
elif method == requests.post:
want_status = 201
elif method == requests.delete:
want_status = 204
if want_status is not None:
if response.status_code != want_status:
raise UnexpectedHttpStatusCode(want_status,
response.status_code)
return response.json()
def check_min_version(self, data=None):
"""
Checks the minimum server version
"""
if data is None:
response = self.request('version')
version = response.get('version')
if version is None:
return False
else:
version = data.get('version')
major, minor, release = version.split('.', 3)
version = (int(major), int(minor), int(release))
return MIN_REQUIRED_VERSION >= version
def ping(self):
"""
Tests connectivity to the currently set avocado-server
This is intentionally a simple method that will only return True if a
request is made, and a response is received from the server.
"""
try:
self.request('version')
except Exception:
return False
return True
def get_api_list(self):
"""
Gets the list of APIs the server makes available to the current user
"""
return self.request('')
#: Global, default connection for ease of use by apps
CONNECTION = None
def get_default():
"""
Returns the global, default connection to avocado-server
:returns: an avocado.core.restclient.connection.Connection instance
"""
global CONNECTION # pylint: disable=W0603
if CONNECTION is None:
CONNECTION = Connection()
return CONNECTION
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
"""
Module with base model functions to manipulate JSON data
"""
import json
class InvalidJSONError(Exception):
"""
Data given to a loader/decoder is not valid JSON
"""
class InvalidResultResponseError(Exception):
"""
Returned result response does not conform to expectation
Even though the result may be a valid json, it may not have the required
or expected information that would normally be sent by avocado-server.
"""
class BaseResponse:
"""
Base class that provides commonly used features for response handling
"""
REQUIRED_DATA = []
def __init__(self, json_data):
self._json_data = json_data
self._data = None
self._load_data()
def _parse_data(self):
try:
self._data = json.loads(self._json_data)
except ValueError:
raise InvalidJSONError(self._json_data)
def _load_data(self):
self._parse_data()
if self.REQUIRED_DATA:
missing_data = []
for data_member in self.REQUIRED_DATA:
if data_member not in self._data:
missing_data.append(data_member)
if missing_data:
missing = ", ".join(missing_data)
msg = "data member(s) missing from response: %s" % missing
raise InvalidResultResponseError(msg)
class ResultResponse(BaseResponse):
"""
Provides a wrapper around an ideal result response
This class should be instantiated with the JSON data received from an
avocado-server, and will check if the required data members are present
and thus the response is well formed.
"""
REQUIRED_DATA = ['count', 'next', 'previous', 'results']
def __init__(self, json_data):
self.count = 0
self.next = None
self.previous = None
self.results = []
super(ResultResponse, self).__init__(json_data)
def _load_data(self):
super(ResultResponse, self)._load_data()
self.count = self._data.get('count')
self.next = self._data.get('next')
self.previous = self._data.get('previous')
self.results = self._data.get('results')
:title: avocado-rest-client
:subtitle: REST client command line tool
:title_upper: AVOCADO
:manual_section: 1
SYNOPSIS
========
avocado-rest-client [-h] [--hostname HOSTNAME] [--port PORT] [--username USERNAME] [--password PASSWORD]
DESCRIPTION
===========
Avocado is a modern test framework that is built on the experience
accumulated with `autotest` (`http://autotest.github.io`).
`avocado-rest-client` is the name of the command line tool that interacts
with `avocado-server`.
`avocado-server` (`http://github.com/avocado-framework/avocado-server`)
is an HTTP server that provides an REST API for results job results and
other features.
For more information about the Avocado project, please check its website:
http://avocado-framework.github.io/
OPTIONS
=======
The following list of options are builtin, application level `avocado-rest-client`
options. Most other options are implemented via plugins and will depend
on them being loaded::
--hostname HOSTNAME Hostname or IP address for the avocado server
--port PORT Port where avocado server is listening on
--username USERNAME Username to authenticate to avocado server
--password PASSWORD Password to give to avocado server
Real use of avocado depends on running avocado subcommands. This the current list
of subcommands::
server inspects the server status and available functionality
To get usage instructions for a given subcommand, run it with `--help`. Example::
$ avocado-rest-client server --help
-l, --list-brief list all records briefly
-s, --status shows the avocado-server status
FILES
=====
::
/etc/avocado/avocado.conf
system wide configuration file
BUGS
====
If you find a bug, please report it over our github page as an issue.
LICENSE
================
Avocado is released under GPLv2 (explicit version)
`http://gnu.org/licenses/gpl-2.0.html`. Even though most of the current code is
licensed under a "and any later version" clause, some parts are specifically
bound to the version 2 of the license and therefore that's the official license
of the prject itself. For more details, please see the LICENSE file in the
project source code directory.
MORE INFORMATION
================
For more information please check Avocado's project website, located at
`http://avocado-framework.github.io/`. There you'll find links to online
documentation, source code and community resources.
AUTHOR
======
Avocado Development Team <avocado-devel@redhat.com>
......@@ -76,7 +76,6 @@ BuildRequires: python3-devel
BuildRequires: python3-docutils
BuildRequires: python3-lxml
BuildRequires: python3-psutil
BuildRequires: python3-requests
BuildRequires: python3-resultsdb_api
BuildRequires: python3-setuptools
BuildRequires: python3-sphinx
......@@ -105,7 +104,6 @@ Requires: gdb
Requires: gdb-gdbserver
Requires: procps-ng
Requires: python3
Requires: python3-requests
Requires: python3-setuptools
Requires: python3-stevedore
Requires: python3-pycdlib
......
#!/usr/bin/env python
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; specifically version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2014
# Author: Cleber Rosa <cleber@redhat.com>
"""
Avocado Journal Replay Utility. Sends test journal data to an Avocado Server.
"""
import sys
import sqlite3
import argparse
import requests
class ArgumentParser(argparse.ArgumentParser):
def __init__(self):
super(ArgumentParser, self).__init__(
prog='avocado-journal-replay',
description=('Avocado Test Journal Replay. Sends test journal data '
'to an Avocado Server'))
self.add_argument('-s', '--server-uri',
help=('Avocado server http URI. Example: '
'http://avocado-server:8000'))
self.add_argument('-u', '--username',
help='Username to authenticate to Avocado Server')
self.add_argument('-p', '--password',
help='Password to authenticate to Avocado Server')
self.add_argument('-n', '--job-name',
help='Name to give to job on the Avocado Server')
self.add_argument('-j', '--journal-path',
help='Path to the journal file (.journal.sqlite)')
class App:
def __init__(self):
self.app_parser = ArgumentParser()
self.args = None
def replay_test_activity(self, job, tag, time, action, status):
action_uri = 'jobs/%s/test_activity' % job
full_uri = "%s/%s/" % (self.args.server_uri, action_uri)
data = {'activity': action,
'status': status,
'job': job,
'time': time,
'test_tag': tag}
r = requests.post(full_uri,
auth=(self.args.username,
self.args.password),
data=data)
if r.status_code == 200:
return True
else:
return False
def create_job(self, unique_id):
action_uri = 'jobs'
full_uri = "%s/%s/" % (self.args.server_uri, action_uri)
data = {'name': self.args.job_name,
'uniqueident': str(unique_id)}
r = requests.post(full_uri,
auth=(self.args.username,
self.args.password),
data=data)
if r.status_code == 201:
return r.json()['id']
else:
print("Failed to add job. reason(s): %s" % r.text)
return False
def replay(self):
journal = sqlite3.connect(self.args.journal_path)
journal_cursor = journal.cursor()
job_unique_id_sql = 'SELECT unique_id FROM job_info'
job_unique_id = journal_cursor.execute(job_unique_id_sql).fetchone()[0]
job_result = self.create_job(job_unique_id)
if not job_result:
return -1
while True:
journal = sqlite3.connect(self.args.journal_path)
journal_cursor = journal.cursor()
backlog_count_sql = "SELECT COUNT(*) FROM test_journal WHERE flushed==0"
backlog_count = journal_cursor.execute(backlog_count_sql).fetchone()[0]
journal_cursor.close()
journal.close()
if backlog_count == 0:
break
entry_sql = ("SELECT tag, time, action, status "
"FROM test_journal WHERE flushed==0 "
"ORDER BY time LIMIT 1")
journal = sqlite3.connect(self.args.journal_path)
journal_cursor = journal.cursor()
entry = journal_cursor.execute(entry_sql).fetchone()
journal_cursor.close()
journal.close()
tag, time, action, status = entry
activity_result = self.replay_test_activity(job_result,
tag,
time,
action,
status)
if activity_result:
flush_sql = ("UPDATE test_journal SET flushed=1 WHERE "
"tag=='%s' AND action=='%s' AND flushed==0")
flush_sql = flush_sql % (tag, action)
journal = sqlite3.connect(self.args.journal_path)
journal_cursor = journal.cursor()
journal_cursor.execute(flush_sql)
journal.commit()
journal_cursor.close()
journal.close()
def run(self):
self.args = self.app_parser.parse_args()
return self.replay()
if __name__ == '__main__':
app = App()
sys.exit(app.run())
#!/usr/bin/env python
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; specifically version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2015
# Author: Cleber Rosa <cleber@redhat.com>
import sys
from avocado.core.restclient.cli.app import App
if __name__ == '__main__':
app = App()
sys.exit(app.run())
import unittest
from avocado.core.restclient import response
class ResultResponseTest(unittest.TestCase):
GOOD_DATA = ('{"count": 1, "next": null, "previous": null, '
'"results": [ { "name": "unknown" } ] }')
BAD_DATA_JSON = '{"count": 1'
BAD_DATA_COUNT = ('{"counter": 1, "next": null, "previous": null, '
'"results": [ { "name": "unknown" } ] }')
BAD_DATA_NEXT = ('{"count": 1, "NEXT": null, "previous": null, '
'"results": [ { "name": "unknown" } ] }')
BAD_DATA_PREVIOUS = ('{"count": 1, "next": null, "prev": null, '
'"results": [ { "name": "unknown" } ] }')
BAD_DATA_RESULTS = '{"count": 1, "next": null, "prev": null}'
def test_good_data(self):
r = response.ResultResponse(self.GOOD_DATA)
self.assertEqual(r.count, 1)
def test_bad_data_json(self):
self.assertRaises(response.InvalidJSONError,
response.ResultResponse,
self.BAD_DATA_JSON)
def test_bad_data_empty(self):
self.assertRaises(response.InvalidJSONError,
response.ResultResponse, '')
def test_bad_data_count(self):
self.assertRaises(response.InvalidResultResponseError,
response.ResultResponse,
self.BAD_DATA_COUNT)
def test_bad_data_next(self):
self.assertRaises(response.InvalidResultResponseError,
response.ResultResponse,
self.BAD_DATA_NEXT)
def test_bad_data_previous(self):
self.assertRaises(response.InvalidResultResponseError,
response.ResultResponse,
self.BAD_DATA_PREVIOUS)
def test_bad_data_results(self):
self.assertRaises(response.InvalidResultResponseError,
response.ResultResponse,
self.BAD_DATA_RESULTS)
if __name__ == '__main__':
unittest.main()
......@@ -30,7 +30,7 @@ def get_long_description():
return req_contents
INSTALL_REQUIREMENTS = ['requests', 'stevedore>=0.14', 'setuptools']
INSTALL_REQUIREMENTS = ['stevedore>=0.14', 'setuptools']
if __name__ == '__main__':
# Force "make develop" inside the "readthedocs.org" environment
......@@ -59,8 +59,7 @@ if __name__ == '__main__':
],
packages=find_packages(exclude=('selftests*',)),
include_package_data=True,
scripts=['scripts/avocado',
'scripts/avocado-rest-client'],
scripts=['scripts/avocado'],
entry_points={
'avocado.plugins.cli': [
'envkeep = avocado.plugins.envkeep:EnvKeep',
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册