cleanup legacy changes

Change-Id: I5a29cee1ed9e3ea18d2e7e659d5e3143d122bf62
This commit is contained in:
hosingh000 2017-07-28 10:46:45 -05:00
parent 113e5618e9
commit 50de35d7e8
161 changed files with 4357 additions and 147 deletions

View File

@ -0,0 +1,17 @@
If you would like to contribute to the development of OpenStack, you must
follow the steps in this page:
http://docs.openstack.org/infra/manual/developers.html
If you already have a good understanding of how the system works and your
OpenStack accounts are set up, you can skip to the development workflow
section of this documentation to learn how changes to OpenStack should be
submitted for review via the Gerrit tool:
http://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Launchpad, not GitHub:
https://bugs.launchpad.net/audit_client

View File

@ -0,0 +1,4 @@
audit_client Style Commandments
===============================================
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/

View File

@ -0,0 +1,6 @@
include AUTHORS
include ChangeLog
exclude .gitignore
exclude .gitreview
global-exclude *.pyc

View File

@ -0,0 +1,19 @@
===============================
audit_client
===============================
This is the Audit Client project
Please feel here a long description which must be at least 3 lines wrapped on
80 cols, so that distribution package maintainers can use it in their packages.
Note that this is a hard requirement.
* Free software: Apache license
* Documentation: http://docs.openstack.org/developer/audit_client
* Source: http://git.openstack.org/cgit/audit_client/audit_client
* Bugs: http://bugs.launchpad.net/audit_client
Features
--------
* TODO

View File

@ -0,0 +1 @@
"""audit_client package."""

View File

@ -0,0 +1 @@
"""api package."""

View File

@ -0,0 +1,219 @@
"""audit module."""
import json
import logging
import threading
import time
import urllib2
from audit_client.api.exceptions.audit_exception import AuditException
from audit_client.api.model.get_audits_result import AuditsResult
from audit_client.api.model.transaction import Transaction
logger = logging.getLogger(__name__)
config = {
'AUDIT_SERVER_URL': None,
'NUM_OF_SEND_RETRIES': None,
'TIME_WAIT_BETWEEN_RETRIES': None,
'SERVICE_NAME': None}
def init(audit_server_url, num_of_send_retries, time_wait_between_retries,
service_name=''):
"""Initialize the audit client.
:param audit_server_url: audit server url
:param num_of_send_retries: number of times to try and send the record
in case of failures.
:param time_wait_between_retries: how much time to wait (in seconds)
between each retry.
"""
if not audit_server_url \
or not num_of_send_retries \
or not time_wait_between_retries:
error_msg = "Error: Fail to initialize audit using following inputs " \
"AUDIT_SERVER_URL={}, NUM_OF_SEND_RETRIES={}, " \
"TIME_WAIT_BETWEEN_RETRIES={}. " \
"One of them is possibly None" \
.format(audit_server_url, num_of_send_retries,
time_wait_between_retries)
logger.error(error_msg)
raise AuditException(error_msg)
config['AUDIT_SERVER_URL'] = audit_server_url
config['NUM_OF_SEND_RETRIES'] = num_of_send_retries
config['TIME_WAIT_BETWEEN_RETRIES'] = time_wait_between_retries
config['SERVICE_NAME'] = service_name
def _validate():
"""# Validate proper initialization of the audit client."""
# if not AUDIT_SERVER_URL or not NUM_OF_SEND_RETRIES
# or not TIME_WAIT_BETWEEN_RETRIES:
if not config['AUDIT_SERVER_URL'] \
or not config['NUM_OF_SEND_RETRIES'] \
or not config['TIME_WAIT_BETWEEN_RETRIES']:
raise AuditException(
"Error: Audit was not initialize correctly. You must first "
"run audit.init(audit_server_url, num_of_send_retries, "
"time_wait_between_retries)")
def audit(timestamp, application_id, tracking_id, transaction_id,
transaction_type, resource_id, service_name, user_id=None,
external_id=None, event_details=None):
"""The method is used to audit transactions.
:param timestamp:
:param application_id:
:param tracking_id:
:param transaction_id:
:param transaction_type:
:param resource_id:
:param service_name:
:param user_id:
:param external_id:
:param event_details:
:return:
"""
thread = threading.Thread(target=_audit_thread, args=(
timestamp, application_id, tracking_id, transaction_id,
transaction_type,
resource_id, service_name, user_id, external_id, event_details))
thread.start()
def get_audits(timestamp_from=None, timestamp_to=None, application_id=None,
tracking_id=None, transaction_id=None, transaction_type=None,
resource_id=None, service_name=None, user_id=None,
external_id=None, event_details=None, limit=None):
"""The method is used to audit transactions.
:param timestamp_from:
:param timestamp_to:
:param application_id:
:param tracking_id:
:param transaction_id:
:param transaction_type:
:param resource_id:
:param service_name:
:param user_id:
:param external_id:
:param event_details:
"""
query = ""
query = _build_query(query, "q.timestamp_from", timestamp_from)
query = _build_query(query, "q.timestamp_to", timestamp_to)
query = _build_query(query, "q.application_id", application_id)
query = _build_query(query, "q.tracking_id", tracking_id)
query = _build_query(query, "q.transaction_id", transaction_id)
query = _build_query(query, "q.transaction_type", transaction_type)
query = _build_query(query, "q.resource_id", resource_id)
query = _build_query(query, "q.service_name", service_name)
query = _build_query(query, "q.user_id", user_id)
query = _build_query(query, "q.external_id", external_id)
query = _build_query(query, "q.event_details", event_details)
query = _build_query(query, "limit", limit)
payload = _get_data(query)
data = json.load(payload)
transactions = []
for transaction in data['transactions']:
timestamp = transaction['timestamp']
user_id = transaction['user_id']
application_id = transaction['application_id']
tracking_id = transaction['tracking_id']
external_id = transaction['external_id']
transaction_id = transaction['transaction_id']
resource_id = transaction['resource_id']
service_name = transaction['service_name']
transaction_type = transaction['transaction_type']
event_details = transaction['event_details']
transactions.append(
Transaction(timestamp, user_id, application_id, tracking_id,
external_id, transaction_id, transaction_type,
event_details, resource_id, service_name))
return AuditsResult(transactions)
def _build_query(query, arg_name, arg_value):
if arg_value is not None:
query = query + "%s=%s&" % (arg_name, arg_value)
return query
# A thread method for sending data to the audit server
# This method is asynchronic in order to prevent blocking
def _audit_thread(timestamp, application_id, tracking_id, transaction_id,
transaction_type, resource_id, service_name, user_id,
external_id, event_details):
# Prepare the data for the audit server
data = {
"timestamp": timestamp,
"application_id": application_id,
"tracking_id": tracking_id,
"transaction_id": transaction_id,
"transaction_type": transaction_type,
"resource_id": resource_id,
"service_name": service_name,
"user_id": user_id,
"external_id": external_id,
"event_details": event_details,
"resource_id": resource_id,
"service_name": service_name
}
_post_data(data)
def _post_data(data):
# Validate that the configuration was initialized
_validate()
# Send the data
req = urllib2.Request(config['AUDIT_SERVER_URL'])
req.add_header('Content-Type', 'application/json')
# Retry to send the data to the audit server
success = False
for retry_number in range(config['NUM_OF_SEND_RETRIES']):
try:
urllib2.urlopen(req, json.dumps(data))
success = True
break
except Exception as error:
time.sleep(config['TIME_WAIT_BETWEEN_RETRIES'])
if not success:
error_msg = "ERROR|CON{}AUDIT001|Fail to send data to [{}]. Tried " \
"a couple of times with no success. Last attempt " \
"error: [{}]".format(config['SERVICE_NAME'],
config['AUDIT_SERVER_URL'],
error.message)
logger.error(error_msg)
raise AuditException(error_msg)
def _get_data(query):
# Validate that the configuration was initialized
_validate()
# Send the data
audit_server_url_with_query = "{}?{}".format(config['AUDIT_SERVER_URL'],
query)
req = urllib2.Request(audit_server_url_with_query)
# Retry to get the data from the audit server
success = False
response = None
for retry_number in range(config['NUM_OF_SEND_RETRIES']):
try:
response = urllib2.urlopen(req)
success = True
break
except Exception as error:
time.sleep(config['TIME_WAIT_BETWEEN_RETRIES'])
if not success:
error_msg = "Fail to get data from [{}]. Tried a couple of times " \
"with no success. Last attempt error: [{}]".\
format(audit_server_url_with_query, error.message)
logger.error(error_msg)
raise AuditException(error_msg)
else:
return response

View File

@ -0,0 +1 @@
"""exceptions package."""

View File

@ -0,0 +1,9 @@
"""audit exception module."""
class AuditException(Exception):
"""AuditException class."""
def __init__(self, error_msg):
"""init method."""
Exception.__init__(self, error_msg)

View File

@ -0,0 +1 @@
"""model package."""

View File

@ -0,0 +1,14 @@
"""get_audit_result module."""
class AuditsResult(object):
"""AuditResult class."""
def __init__(self, transactions):
"""init method."""
self.transactions = transactions
def __str__(self):
"""string representation of the object."""
return "AuditsResult[ " \
"transactions: {}]".format(self.transactions)

View File

@ -0,0 +1,49 @@
"""transaction module."""
class Transaction(object):
"""transaction class."""
def __init__(self, timestamp, user_id, application_id, tracking_id,
external_id, transaction_id, transaction_type, event_details,
resource_id, service_name):
"""init method."""
self.timestamp = timestamp
self.user_id = user_id
self.application_id = application_id
self.tracking_id = tracking_id
self.external_id = external_id
self.transaction_id = transaction_id
self.transaction_type = transaction_type
self.event_details = event_details
self.resource_id = resource_id
self.service_name = service_name
def __str__(self):
"""string representation of the object."""
return "Transaction:[" \
"timestamp={}, " \
"user_id={}, " \
"application_id={}, " \
"tracking_id={}, " \
"external_id={}, " \
"transaction_id={}, " \
"transaction_type={}, " \
"event_details={}, " \
"resource_id={}, " \
"service_name={}]" \
.format(
self.timestamp,
self.user_id,
self.application_id,
self.tracking_id,
self.external_id,
self.transaction_id,
self.transaction_type,
self.event_details,
self.resource_id,
self.service_name)
def __repr__(self):
"""string representation of the object."""
return str(self)

View File

@ -0,0 +1 @@
"""examples package."""

View File

@ -0,0 +1,22 @@
"""get_audits module."""
from audit_client.api import audit
audit_server_url = "http://127.0.0.1:8776/v1/audit/transaction"
num_of_send_retries = 3
time_wait_between_retries = 1
audit.init(audit_server_url, num_of_send_retries, time_wait_between_retries)
response = audit.get_audits(timestamp_from=1, timestamp_to=5,
application_id="application_id_1",
tracking_id="tracking_id_1",
transaction_id="transaction_id_1",
transaction_type="transaction_type_1",
resource_id="resource_id_1",
service_name="service_name_1", user_id="user_id_1",
external_id="external_id_1",
event_details="event_details_1", status="status_1",
limit=10)
print(response)
response = audit.get_audits(timestamp_from=1, timestamp_to=5, limit=10)
print(response)

View File

@ -0,0 +1,52 @@
"""put_audit module."""
from audit_client.api import audit
audit_server_url = "http://127.0.0.1:8776/v1/audit/transaction"
num_of_send_retries = 3
time_wait_between_retries = 1
audit.init(audit_server_url, num_of_send_retries, time_wait_between_retries)
audit.audit(1, "application_id_1", "tracking_id_1", "transaction_id_1",
"transaction_type_1", "resource_id_1", "service_name_1",
"user_id_1", "external_id_1", "event_details_1", "status_1")
print("audit1")
audit.audit(2, "application_id_2", "tracking_id_2", "transaction_id_2",
"transaction_type_2", "resource_id_2", "service_name_2",
"user_id_2", "external_id_2", "event_details_2", "status_2")
print("audit2")
audit.audit(3, "application_id_3", "tracking_id_3", "transaction_id_3",
"transaction_type_3", "resource_id_3", "service_name_3",
"user_id_3", "external_id_3", "event_details_3", "status_3")
print("audit3")
audit.audit(4, "application_id_4", "tracking_id_4", "transaction_id_4",
"transaction_type_4", "resource_id_4", "service_name_4",
"user_id_4", "external_id_4", "event_details_4", "status_4")
print("audit4")
audit.audit(5, "application_id_5", "tracking_id_5", "transaction_id_5",
"transaction_type_5", "resource_id_5", "service_name_5",
"user_id_5", "external_id_5", "event_details_5", "status_5")
print("audit5")
audit.audit(6, "application_id_6", "tracking_id_6", "transaction_id_6",
"transaction_type_6", "resource_id_6", "service_name_6",
"user_id_6", "external_id_6", "event_details_6", "status_6")
print("audit6")
audit.audit(7, "application_id_7", "tracking_id_7", "transaction_id_7",
"transaction_type_7", "resource_id_7", "service_name_7",
"user_id_7", "external_id_7", "event_details_7", "status_7")
print("audit7")
audit.audit(8, "application_id_8", "tracking_id_8", "transaction_id_8",
"transaction_type_8", "resource_id_8", "service_name_8",
"user_id_8", "external_id_8", "event_details_8", "status_8")
print("audit8")
audit.audit(9, "application_id_9", "tracking_id_9", "transaction_id_9",
"transaction_type_9", "resource_id_9", "service_name_9",
"user_id_9", "external_id_9", "event_details_9", "status_9")
print("audit9")
audit.audit(10, "application_id_10", "tracking_id_10", "transaction_id_10",
"transaction_type_10", "resource_id_10", "service_name_10",
"user_id_10", "external_id_10", "event_details_10", "status_10")
print("audit10")
audit.audit(11, "application_id_11", "tracking_id_11", "transaction_id_11",
"transaction_type_11", "resource_id_11", "service_name_11",
"user_id_11", "external_id_11", "event_details_11", "status_11")
print("audit11")

View File

@ -0,0 +1 @@
"""test package."""

View File

@ -0,0 +1 @@
"""api package."""

View File

@ -0,0 +1 @@
"""model package."""

View File

@ -0,0 +1,16 @@
"""test_get_audits_result module."""
from audit_client.api.model.get_audits_result import AuditsResult
import unittest
class Test(unittest.TestCase):
"""test get audits result class."""
def test_init_AuditsResult(self):
"""test init method."""
transactions = []
audit_result = AuditsResult(transactions)
self.assertEqual(str(audit_result),
"AuditsResult[ transactions: {}]".format(
transactions))

View File

@ -0,0 +1,69 @@
"""test transactions module."""
import unittest
from audit_client.api.model.transaction import Transaction
class Test(unittest.TestCase):
"""test transactions class."""
def test_init_AuditsResult(self):
"""test init method."""
timestamp = 111
user_id = "user_id_1"
application_id = "application_id_1"
tracking_id = "tracking_id_1"
external_id = "external_id_1"
transaction_id = "transaction_id_1"
transaction_type = "transaction_type_1"
event_details = "event_details_1"
resource_id = "resource_id_1"
service_name = "service_name_1"
transaction = Transaction(timestamp, user_id, application_id,
tracking_id, external_id, transaction_id,
transaction_type, event_details,
resource_id, service_name)
self.assertEqual(str(transaction), "Transaction:["
"timestamp={}, "
"user_id={}, "
"application_id={}, "
"tracking_id={}, "
"external_id={}, "
"transaction_id={}, "
"transaction_type={}, "
"event_details={}, "
"resource_id={}, "
"service_name={}]".format(
timestamp,
user_id,
application_id,
tracking_id,
external_id,
transaction_id,
transaction_type,
event_details,
resource_id,
service_name))
self.assertEqual(transaction.__repr__(), "Transaction:["
"timestamp={}, "
"user_id={}, "
"application_id={}, "
"tracking_id={}, "
"external_id={}, "
"transaction_id={}, "
"transaction_type={}, "
"event_details={}, "
"resource_id={}, "
"service_name={}]".format(
timestamp,
user_id,
application_id,
tracking_id,
external_id,
transaction_id,
transaction_type,
event_details,
resource_id,
service_name))

View File

@ -0,0 +1,265 @@
"""test_audit module."""
import json
import threading
import unittest
import urllib2
from mock import patch
from audit_client.api import audit
from audit_client.api.exceptions.audit_exception import AuditException
class Test(unittest.TestCase):
"""test audit class."""
data = {
'transactions': [
{
'timestamp': 111,
'user_id': 'user_id_1',
'application_id': 'application_id_1',
'tracking_id': 'tracking_id_1',
'external_id': 'external_id_1',
'transaction_id': 'transaction_id_1',
'transaction_type': 'transaction_type_1',
'event_details': 'event_details_1',
'resource_id': "resource_id_1",
'service_name': "service_name_1"
}, {
'timestamp': 111,
'user_id': 'user_id_2',
'application_id': 'application_id_2',
'tracking_id': 'tracking_id_2',
'external_id': 'external_id_2',
'transaction_id': 'transaction_id_2',
'transaction_type': 'transaction_type_2',
'event_details': 'event_details_2',
'resource_id': "resource_id_2",
'service_name': "service_name_3"
}, {
'timestamp': 111,
'user_id': 'user_id_3',
'application_id': 'application_id_3',
'tracking_id': 'tracking_id_3',
'external_id': 'external_id_3',
'transaction_id': 'transaction_id_3',
'transaction_type': 'transaction_type_3',
'event_details': 'event_details_3',
'resource_id': "resource_id_3",
'service_name': "service_name_3"
}
]
}
def test_init(self):
"""test init method.
test that init doesn't throw and exeception/error
and that the gloabl variables are set correctly
"""
audit_server_url = "audit_server_url_1"
num_of_send_retries = 5
time_wait_between_retries = 10
audit.init(audit_server_url, num_of_send_retries,
time_wait_between_retries)
self.assertEqual(audit.config['AUDIT_SERVER_URL'], audit_server_url)
self.assertEqual(audit.config['NUM_OF_SEND_RETRIES'],
num_of_send_retries)
self.assertEqual(audit.config['TIME_WAIT_BETWEEN_RETRIES'],
time_wait_between_retries)
#
def test_init_with_audit_server_url_empty(self):
"""test that init throws an exception when audit_server_url is None."""
audit_server_url = None
num_of_send_retries = 5
time_wait_between_retries = 10
self.assertRaises(AuditException, audit.init, audit_server_url,
num_of_send_retries, time_wait_between_retries)
def test_init_with_num_of_send_retries_empty(self):
"""test init method.
test that init throws an exception when num_of_send_retries is None
"""
audit_server_url = "audit_server_url_1"
num_of_send_retries = None
time_wait_between_retries = 10
self.assertRaises(AuditException, audit.init, audit_server_url,
num_of_send_retries, time_wait_between_retries)
def test_init_with_time_wait_between_retries_empty(self):
"""test init method.
Test that init throws an exception when
time_wait_between_retries is None.
"""
audit_server_url = "audit_server_url_1"
num_of_send_retries = 5
time_wait_between_retries = None
self.assertRaises(AuditException, audit.init, audit_server_url,
num_of_send_retries, time_wait_between_retries)
def test_validate(self):
"""test validate method.
test that _validate doesn't throw and exception when all variables
are populated
"""
audit_server_url = "audit_server_url_1"
num_of_send_retries = 5
time_wait_between_retries = 10
audit.init(audit_server_url, num_of_send_retries,
time_wait_between_retries)
audit._validate()
def test_validate_with_audit_server_url_empty(self):
"""test validate with audit_server_url.
test that _validate throw and exception when AUDIT_SERVER_URL
is None.
"""
audit.config['AUDIT_SERVER_URL'] = None
audit.config['NUM_OF_SEND_RETRIES'] = 5
audit.config['TIME_WAIT_BETWEEN_RETRIES'] = 10
self.assertRaises(AuditException, audit._validate)
def test_validate_with_num_of_send_retries_empty(self):
"""test validate with num of send_retries_empty.
test that _validate throw and exception when NUM_OF_SEND_RETRIES
is None
"""
audit.config['AUDIT_SERVER_URL'] = "audit_server_url_1"
audit.config['NUM_OF_SEND_RETRIES'] = None
audit.config['TIME_WAIT_BETWEEN_RETRIES'] = 10
self.assertRaises(AuditException, audit._validate)
def test_validate_with_time_wait_between_retries_empty(self):
"""test validate with ime_wait_between_retries_empty.
test that _validate throw and exception when
TIME_WAIT_BETWEEN_RETRIES is None
"""
audit.config['AUDIT_SERVER_URL'] = "audit_server_url_1"
audit.config['NUM_OF_SEND_RETRIES'] = 5
audit.config['TIME_WAIT_BETWEEN_RETRIES'] = None
self.assertRaises(AuditException, audit._validate)
@patch.object(urllib2, 'urlopen')
@patch.object(json, 'load', return_value=data)
def test_get_audits(self, mock_urlopen, mock_load):
"""test get_audits."""
audit_server_url = "audit_server_url_1"
num_of_send_retries = 5
time_wait_between_retries = 10
audit.init(audit_server_url, num_of_send_retries,
time_wait_between_retries)
result = audit.get_audits()
self.assertEqual(len(result.transactions), 3)
@patch.object(urllib2, 'urlopen')
@patch.object(json, 'load')
def test_audit_thread(self, mock_urlopen, mock_load):
"""Test audit_thread is executed with no exceptions."""
audit_server_url = "audit_server_url_1"
num_of_send_retries = 5
time_wait_between_retries = 10
audit.init(audit_server_url, num_of_send_retries,
time_wait_between_retries)
timestamp = 111
application_id = 'application_id_1'
tracking_id = 'tracking_id_1'
transaction_id = 'transaction_id_1'
transaction_type = 'transaction_type_1'
user_id = 'user_id_1'
external_id = 'external_id_1'
event_details = 'event_details_1'
resource_id = 'resource_id_1'
service_name = 'service_name_1'
audit._audit_thread(timestamp, application_id, tracking_id,
transaction_id, transaction_type,
resource_id, service_name, user_id, external_id,
event_details)
@patch.object(urllib2, 'urlopen')
@patch.object(json, 'load')
def test_post_data(self, mock_urlopen, mock_load):
"""test that post_data is executed with no exceptions."""
audit_server_url = "audit_server_url_1"
num_of_send_retries = 5
time_wait_between_retries = 10
audit.init(audit_server_url, num_of_send_retries,
time_wait_between_retries)
audit._post_data(None)
@patch.object(urllib2, 'urlopen', side_effect=Exception)
@patch.object(json, 'load')
def test_post_data_with_retry_exception(self, mock_urlopen, mock_load):
"""test post data with retry_exception.
test that post_data throws an exception when trying to send
a data for num_of_send_retries
"""
audit_server_url = "audit_server_url_1"
num_of_send_retries = 1
time_wait_between_retries = 1
audit.init(audit_server_url, num_of_send_retries,
time_wait_between_retries)
self.assertRaises(AuditException, audit._post_data, None)
def test_build_query(self):
"""test that build_query is exceuted with no exception."""
query = "query_1"
arg_name = "arg_1"
arg_value = "arg_value_1"
expected_query = query + "%s=%s&" % (arg_name, arg_value)
returned_query = audit._build_query(query, arg_name, arg_value)
self.assertEqual(returned_query, expected_query)
def test_build_query_with_empty_arg_value(self):
"""test build query with empty_arg_value.
test that build_query throws an exception when called
with empty arg_value.
"""
query = "query_1"
arg_name = "arg_1"
arg_value = None
expected_query = query
returned_query = audit._build_query(query, arg_name, arg_value)
self.assertEqual(returned_query, expected_query)
@patch.object(threading, 'Thread')
def test_audit(self, mock_thread):
"""test that audit is exceuted with no exception."""
audit_server_url = "audit_server_url_1"
num_of_send_retries = 1
time_wait_between_retries = 1
audit.init(audit_server_url, num_of_send_retries,
time_wait_between_retries)
timestamp = 111
application_id = 'application_id_1'
tracking_id = 'tracking_id_1'
transaction_id = 'transaction_id_1'
transaction_type = 'transaction_type_1'
user_id = 'user_id_1'
external_id = 'external_id_1'
event_details = 'event_details_1'
resource_id = 'resource_id_1'
service_name = 'service_name_1'
audit.audit(timestamp, application_id, tracking_id, transaction_id,
transaction_type, resource_id, service_name, user_id,
external_id, event_details)
def test_get_data(self):
"""test get data."""
audit_server_url = "audit_server_url_1"
num_of_send_retries = 1
time_wait_between_retries = 1
audit.init(audit_server_url, num_of_send_retries,
time_wait_between_retries)
query = "query1"
self.assertRaises(AuditException, audit._get_data, query)

View File

@ -0,0 +1,2 @@
[python: **.py]

View File

@ -0,0 +1,4 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.

View File

@ -0,0 +1,46 @@
[metadata]
name = audit_client
summary = This is the Audit Client project
description-file =
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = http://www.openstack.org/
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
[files]
packages =
audit_client
[build_sphinx]
source-dir = doc/source
build-dir = doc/build
all_files = 1
[upload_sphinx]
upload-dir = doc/build/html
[compile_catalog]
directory = audit_client/locale
domain = audit_client
[update_catalog]
domain = audit_client
output_dir = audit_client/locale
input_file = audit_client/locale/audit_client.pot
[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext
mapping_file = babel.cfg
output_file = audit_client/locale/audit_client.pot

View File

@ -0,0 +1,33 @@
"""setup.py module."""
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# 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.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
from setuptools import find_packages
from setuptools import setup
setup(
name='audit_client',
version='0.1',
description='',
author='',
author_email='',
zip_safe=False,
include_package_data=True,
packages=find_packages(),
install_requires=[]
)

View File

@ -0,0 +1,9 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
# Hacking already pins down pep8, pyflakes and flake8
hacking<0.11,>=0.10.0
testrepository>=0.0.18
mock<1.1.0,>=1.0
coverage>=3.6

View File

@ -0,0 +1,22 @@
[tox]
envlist = py27, cover, pep8
[testenv]
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
install_command = pip install -U {opts} {packages}
commands = python setup.py testr
[testenv:cover]
#omitting rds/api/app.py and rds/examples/api/functional_test.py
#since they have no need for unit test
commands =
python setup.py testr --slowest --coverage --omit=audit_client/examples/*
coverage report --omit=audit_client/examples/*
[testenv:pep8]
#cannot handle and 'H102 Apache 2.0 license header not found' and
#'H202 assertRaises Exception too broad'
#since it requires business code changes
commands = flake8

View File

@ -1 +0,0 @@
aic-orm-keystone/* opt/app/orm/keystone_utils

View File

@ -1,5 +0,0 @@
aic-orm-keystone (3.5.0) stable; urgency=low
* this is release 3.5.0
-- Jenkins job <jenkins@unknown> Thu, 24 Jun 2016 14:48:02 +0000

View File

@ -1 +0,0 @@
9

View File

@ -1,13 +0,0 @@
Source: aic-orm-keystone
Section: unknown
Priority: optional
Maintainer: orm <orm@intl.att.com>
Build-Depends: debhelper (>= 8.0.0)
Standards-Version: 3.9.4
XS-Python-Version: >= 2.7
Homepage: <insert the upstream URL, if relevant>
Package: aic-orm-keystone
Architecture: any
Depends: python-keystoneclient (>= 1.2.0), python-requests (>= 2.2.0)
Description: aic-orm-keystone application for ORM

View File

@ -1,34 +0,0 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: aic-orm-keystone
Source: <url://example.com>
Files: *
Copyright: <years> <put author's name and email here>
<years> <likewise for another author>
License: GPL-3.0+
Files: debian/*
Copyright: 2016 root <root@unknown>
License: GPL-3.0+
License: GPL-3.0+
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 3 of the License, or
(at your option) any later version.
.
This package 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 the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
# Please also look if there are files or directories which have a
# different copyright/license attached and list them here.
# Please avoid to pick license terms that are more restrictive than the
# packaged work, as it may make Debian's contributions unacceptable upstream.

View File

@ -1,4 +0,0 @@
#!/bin/bash
#rm -rf /opt/app/orm/keystone_utils
#echo "Deleting /opt/app/orm/keystone_utils directory."

View File

@ -1,8 +0,0 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
%:
dh $@ --buildsystem=python_distutils -D aic-orm-keystone

View File

@ -1 +0,0 @@
3.0 (native)

View File

@ -1,21 +0,0 @@
Metadata-Version: 1.1
Name: keystone-utils
Version: 0.1
Summary: keyKeystone utils
Home-page: http://www.openstack.org/
Author: OpenStack
Author-email: openstack-dev@lists.openstack.org
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Environment :: OpenStack
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4

View File

@ -1,25 +0,0 @@
MANIFEST.in
README.rst
setup.cfg
setup.py
keystone_utils/__init__.py
keystone_utils/tokens.py
keystone_utils.egg-info/PKG-INFO
keystone_utils.egg-info/SOURCES.txt
keystone_utils.egg-info/dependency_links.txt
keystone_utils.egg-info/not-zip-safe
keystone_utils.egg-info/pbr.json
keystone_utils.egg-info/top_level.txt
keystone_utils/tests/__init__.py
keystone_utils/tests/unit/__init__.py
keystone_utils/tests/unit/test_tokens.py
mock_keystone/__init__.py
mock_keystone/keystoneclient/__init__.py
mock_keystone/keystoneclient/exceptions.py
mock_keystone/keystoneclient/v2_0/__init__.py
mock_keystone/keystoneclient/v2_0/client.py
mock_keystone/keystoneclient/v3/__init__.py
mock_keystone/keystoneclient/v3/client.py
mock_keystone/orm_common/__init__.py
mock_keystone/orm_common/utils/__init__.py
mock_keystone/orm_common/utils/dictator.py

View File

@ -1 +0,0 @@
{"is_release": false, "git_version": ""}

View File

@ -1,2 +0,0 @@
keystone_utils
mock_keystone

View File

@ -0,0 +1 @@
recursive-include public *

View File

@ -0,0 +1,26 @@
Listen 8776
<VirtualHost *:8776>
WSGIDaemonProcess audit_server user=orm group=orm threads=5
WSGIScriptAlias / /opt/app/orm/audit_server/audit_server.wsgi
<Location /v1/audit/logs>
Order deny,allow
Deny from all
Allow from localhost
</Location>
<Location /v1/audit/configuration>
Order deny,allow
Deny from all
Allow from localhost
</Location>
<Directory /opt/app/orm/audit_server/>
WSGIProcessGroup audit_server
WSGIApplicationGroup %{GLOBAL}
Require all granted
Allow from all
</Directory>
</VirtualHost>

View File

@ -0,0 +1,2 @@
from pecan.deploy import deploy
application = deploy('/opt/app/orm/audit_server/config.py')

View File

@ -0,0 +1 @@
"""root package."""

View File

@ -0,0 +1,34 @@
"""app module."""
import logging
import os
from pecan import make_app
from pecan.commands import CommandRunner
from audit_server import model
from audit_server.storage import factory
logger = logging.getLogger(__name__)
def setup_app(config):
"""setup method."""
model.init_model()
app_conf = dict(config.app)
factory.database_url = config.database.url
factory.echo_statements = config.database.echo_statements
app = make_app(
app_conf.pop('root'),
logging=getattr(config, 'logging', {}),
**app_conf
)
logger.info('Starting Audit...')
return app
def main():
dir_name = os.path.dirname(__file__)
drive, path_and_file = os.path.splitdrive(dir_name)
path, filename = os.path.split(path_and_file)
runner = CommandRunner()
runner.run(['serve', path+'/config.py'])

View File

@ -0,0 +1 @@
"""controllers package."""

View File

@ -0,0 +1,10 @@
"""root controller module."""
from audit_server.controllers.v1 import root as v1
class RootController(object):
"""root controller."""
v1 = v1.V1Controller()

View File

@ -0,0 +1 @@
"""v1 package."""

View File

@ -0,0 +1,13 @@
"""audit controller module."""
from audit_server.controllers.v1 import configuration
from audit_server.controllers.v1 import logs
from audit_server.controllers.v1 import transaction
class AuditController(object):
"""audit controller."""
transaction = transaction.TransactionController()
logs = logs.LogsController()
configuration = configuration.ConfigurationController()

View File

@ -0,0 +1,49 @@
"""base module for base wsme types and client side errors."""
import wsme
from wsme import types as wtypes
class ClientSideError(wsme.exc.ClientSideError):
"""base client side error."""
def __init__(self, error, status_code=400):
"""init method..
:param error: error message to show to the client.
:param status_code: status code to show to the client.
"""
super(ClientSideError, self).__init__(error, status_code)
class InputValueError(ClientSideError):
"""input value error."""
def __init__(self, name, value, status_code=400):
"""init method..
:param name: the input name for which an error was raised.
:param value: the input value for which an error was raised.
:param status_code: status code to show to the client.
"""
super(InputValueError, self).__init__(
"Invalid value for input {} : {}".format(name, value), status_code)
class EntityNotFoundError(ClientSideError):
"""entity not found error."""
def __init__(self, entity_id):
"""init method..
:param entity_id: the id for which an entity was not found.
"""
super(EntityNotFoundError, self).__init__(
"Entity not found for {}".format(entity_id), status_code=404)
class Base(wtypes.DynamicBase):
"""base class for wsme types."""
pass

View File

@ -0,0 +1,28 @@
"""Configuration rest API input module."""
import logging
from orm_common.utils import utils
from pecan import conf
from pecan import rest
from wsmeext.pecan import wsexpose
logger = logging.getLogger(__name__)
class ConfigurationController(rest.RestController):
"""Configuration controller."""
@wsexpose(str, str, status_code=200)
def get(self, dump_to_log='false'):
"""get method.
:param dump_to_log: A boolean string that says whether the
configuration should be written to log
:return: A pretty string that contains the service's configuration
"""
logger.info("Get configuration...")
dump = dump_to_log.lower() == 'true'
utils.set_utils_conf(conf)
result = utils.report_config(conf, dump, logger)
return result

View File

@ -0,0 +1,65 @@
import logging
from pecan import rest
import wsme
from wsmeext.pecan import wsexpose
logger = logging.getLogger(__name__)
class LogChangeResultWSME(wsme.types.DynamicBase):
"""log change result wsme type."""
result = wsme.wsattr(str, mandatory=True, default=None)
def __init__(self, **kwargs):
""""init method."""
super(LogChangeResult, self).__init__(**kwargs)
class LogChangeResult(object):
"""log change result type."""
def __init__(self, result):
""""init method."""
self.result = result
class LogsController(rest.RestController):
"""Logs Audit controller."""
@wsexpose(LogChangeResultWSME, str, status_code=201,
rest_content_types='json')
def put(self, level):
"""update log level.
:param level: the log level text name
:return:
"""
logger.info("Changing log level to [{}]".format(level))
try:
log_level = logging._levelNames.get(level.upper())
if log_level is not None:
self._change_log_level(log_level)
result = "Log level changed to {}.".format(level)
logger.info(result)
else:
raise Exception(
"The given log level [{}] doesn't exist.".format(level))
except Exception as e:
result = "Fail to change log_level. Reason: {}".format(
e.message)
logger.error(result)
return LogChangeResult(result)
@staticmethod
def _change_log_level(log_level):
path = __name__.split('.')
if len(path) > 0:
root = path[0]
root_logger = logging.getLogger(root)
root_logger.setLevel(log_level)
else:
logger.info("Fail to change log_level to [{}]. "
"the given log level doesn't exist.".format(log_level))

View File

@ -0,0 +1,10 @@
"""v1 controller module."""
from audit_server.controllers.v1 import audit
class V1Controller(object):
"""v1 controller."""
audit = audit.AuditController()

View File

@ -0,0 +1,203 @@
"""transaction controller module."""
from audit_server.model.transaction import Model as TransactionModel
from audit_server.model.transaction_query import Model as QueryModel
from audit_server.services import transaction as transaction_service
import base
import logging
from pecan import rest
import wsme
from wsme import types as wtypes
from wsmeext.pecan import wsexpose
logger = logging.getLogger(__name__)
class Transaction(base.Base):
"""transaction type."""
timestamp = wsme.wsattr(long, mandatory=True)
user_id = wsme.wsattr(wtypes.text, mandatory=False, default=None)
application_id = wsme.wsattr(wtypes.text, mandatory=True)
tracking_id = wsme.wsattr(wtypes.text, mandatory=True)
external_id = wsme.wsattr(wtypes.text, mandatory=False, default=None)
transaction_id = wsme.wsattr(wtypes.text, mandatory=True)
transaction_type = wsme.wsattr(wtypes.text, mandatory=True)
event_details = wsme.wsattr(wtypes.text, mandatory=False, default=None)
resource_id = wsme.wsattr(wtypes.text, mandatory=True)
service_name = wsme.wsattr(wtypes.text, mandatory=True)
def __init__(self, **kwargs):
"""init method."""
super(Transaction, self).__init__(**kwargs)
def to_model(self):
"""transform the Transaction to a TransactionModel."""
return TransactionModel(self.timestamp,
self.user_id,
self.application_id,
self.tracking_id,
self.external_id,
self.transaction_id,
self.transaction_type,
self.event_details,
self.resource_id,
self.service_name)
def __str__(self):
"""return a string representation of the object."""
return "Transaction:[ " \
"timestamp={}, " \
"user_id={}, " \
"application_id={}, " \
"tracking_id={}, " \
"external_id={}, " \
"transaction_id={}," \
"transaction_type={}, " \
"event_details={}, " \
"resource_id={}," \
"service_name={}]".format(self.timestamp,
self.user_id,
self.application_id,
self.tracking_id,
self.external_id,
self.transaction_id,
self.transaction_type,
self.event_details,
self.resource_id,
self.service_name)
class Query(base.Base):
"""query type."""
timestamp_from = wsme.wsattr(long, mandatory=False, default=None)
timestamp_to = wsme.wsattr(long, mandatory=False, default=None)
user_id = wsme.wsattr(wtypes.text, mandatory=False, default=None)
application_id = wsme.wsattr(wtypes.text, mandatory=False, default=None)
tracking_id = wsme.wsattr(wtypes.text, mandatory=False, default=None)
external_id = wsme.wsattr(wtypes.text, mandatory=False, default=None)
transaction_id = wsme.wsattr(wtypes.text, mandatory=False, default=None)
transaction_type = wsme.wsattr(wtypes.text, mandatory=False, default=None)
event_details = wsme.wsattr(wtypes.text, mandatory=False, default=None)
resource_id = wsme.wsattr(wtypes.text, mandatory=False, default=None)
service_name = wsme.wsattr(wtypes.text, mandatory=False, default=None)
def __init__(self, **kwargs):
"""init method."""
super(Query, self).__init__(**kwargs)
def to_model(self):
"""transform the Query to a QueryModel."""
return QueryModel(self.timestamp_from,
self.timestamp_to,
self.user_id,
self.application_id,
self.tracking_id,
self.external_id,
self.transaction_id,
self.transaction_type,
self.event_details,
self.resource_id,
self.service_name)
def __str__(self):
"""return a string representation of the object."""
return "TransactionQuery:[ " \
"timestamp_from={}, " \
"timestamp_to={}, " \
"user_id={}, " \
"application_id={}, " \
"tracking_id={}, " \
"external_id={}, " \
"transaction_id={}," \
"transaction_type={}, " \
"event_details={}, " \
"resource_id={}," \
"service_name={}]".format(self.timestamp_from,
self.timestamp_to,
self.user_id,
self.application_id,
self.tracking_id,
self.external_id,
self.transaction_id,
self.transaction_type,
self.event_details,
self.resource_id,
self.service_name)
class QueryResult(base.Base):
"""query result type."""
transactions = wsme.wsattr([Transaction], mandatory=False, default=None)
def __init__(self, **kwargs):
""""init method."""
super(QueryResult, self).__init__(**kwargs)
class TransactionController(rest.RestController):
"""Transaction Audit controller."""
@wsexpose(QueryResult, Query, int, str, rest_content_types='json')
def get_all(self, q=None, limit=10, marker=None):
"""get all transactions that meet the given query.
:param q: the query to use in order to search for relevant.
transactions.
:param limit: the maximun number of transactions to return.
:param marker: a place order for pagination (not implemented yet).
Example of usage:
http://127.0.0.1:8777/v1/audit/transaction?q.timestamp_from=1111&q.
timestamp_to=5555&q.user_id=user1&q.application_id=SSP&limit=15&
marker=1
"""
logger.debug("Getting audit records...start")
logger.info(
"Getting audit records for the query:[{}]...start".format(q))
model = None
if q is not None:
model = q.to_model()
# page or marker (the last row in the previous page)
query_result = transaction_service.get_transactions(model, limit,
marker)
logger.debug("Getting audit records...end")
return query_result
"""
"""
@wsexpose(None, body=Transaction, status_code=201,
rest_content_types='json')
def post(self, transaction_input):
"""add a new transaction.
:param transaction_input: the new transaction values
Example of usage:
http://127.0.0.1:8777/v1/audit/transaction
Headers: Content-Type=application/json
Body:{
"timestamp":111,
"user_id":"user1",
"application_id":"application_id1",
"tracking_id":"tracking_id1",
"external_id":"external_id1",
"transaction_id":"transaction_id1",
"transaction_type":"transaction_type1",
"event_details":"event_details1",
"status":"status1",
"resource_id":"resource_id1",
"service_name":"service_name1"
}
"""
logger.debug("Posting new audit record...start")
logger.info("Posting new audit record: [{}]".format(
transaction_input))
model = transaction_input.to_model()
transaction_service.add_transaction(model)
logger.debug("Auditing record...end")

View File

@ -0,0 +1,13 @@
"""Utils module mock."""
def report_config(conf, dump=False):
"""Mock report_config function."""
pass
def set_utils_conf(conf):
"""Mock set_utils_conf function."""
pass

View File

@ -0,0 +1,16 @@
"""model package."""
from pecan import conf # noqa
def init_model():
"""Thi is a stub method which is called at application startup time.
If you need to bind to a parsed database configuration, set up tables or
ORM classes, or perform any database initialization, this is the
recommended place to do it.
For more information working with databases, and some common recipes,
see http://pecan.readthedocs.org/en/latest/databases.html
"""
pass

View File

@ -0,0 +1,47 @@
"""transaction model module."""
class Model(object):
"""transaction model."""
def __init__(self, timestamp, user_id, application_id, tracking_id,
external_id, transaction_id, transaction_type, event_details,
resource_id, service_name):
"""init method."""
self.timestamp = timestamp
self.user_id = user_id
self.application_id = application_id
self.tracking_id = tracking_id
self.external_id = external_id
self.transaction_id = transaction_id
self.transaction_type = transaction_type
self.event_details = event_details
self.resource_id = resource_id
self.service_name = service_name
def __str__(self):
"""return a string representation of the object."""
return "Transaction:[ " \
"timestamp={}, " \
"user_id={}, " \
"application_id={}, " \
"tracking_id={}, " \
"external_id={}, " \
"transaction_id={}," \
"transaction_type={}, " \
"event_details={}, " \
"resource_id={}," \
"service_name={}]".format(self.timestamp,
self.user_id,
self.application_id,
self.tracking_id,
self.external_id,
self.transaction_id,
self.transaction_type,
self.event_details,
self.resource_id,
self.service_name)
def __repr__(self):
"""return a string representation of the transaction object."""
return str(self)

View File

@ -0,0 +1,46 @@
"""transaction_query model module."""
class Model(object):
"""transaction query model."""
def __init__(self, timestamp_from, timestamp_to, user_id, application_id,
tracking_id, external_id, transaction_id, transaction_type,
event_details, resource_id, service_name):
"""init method."""
self.timestamp_from = timestamp_from
self.timestamp_to = timestamp_to
self.user_id = user_id
self.application_id = application_id
self.tracking_id = tracking_id
self.external_id = external_id
self.transaction_id = transaction_id
self.transaction_type = transaction_type
self.event_details = event_details
self.resource_id = resource_id
self.service_name = service_name
def __str__(self):
"""return a string representation of the object."""
return "Transaction:[ " \
"timestamp_from={}, " \
"timestamp_to={}" \
"user_id={}, " \
"application_id={}, " \
"tracking_id={}, " \
"external_id={}, " \
"transaction_id={}," \
"transaction_type={}, " \
"event_details={}, " \
"resource_id={}," \
"service_name={}]".format(self.timestamp_from,
self.timestamp_to,
self.user_id,
self.application_id,
self.tracking_id,
self.external_id,
self.transaction_id,
self.transaction_type,
self.event_details,
self.resource_id,
self.service_name)

View File

@ -0,0 +1,14 @@
"""transaction_query_result model module."""
class Model(object):
"""transaction query result model."""
def __init__(self, transactions):
"""init method."""
self.transactions = transactions
def __str__(self):
"""return a string representation of the object."""
return "TransactionQueryResult:[ " \
"transactions={}]".format(self.transactions)

View File

@ -0,0 +1 @@
"""services package."""

View File

@ -0,0 +1,7 @@
"""base module for all services, holds errors."""
class Error(Exception):
"""base error class."""
pass

View File

@ -0,0 +1,35 @@
"""transaction service module."""
import logging
from audit_server.model.transaction_query_result import \
Model as QueryResultModel
from audit_server.storage import factory
logger = logging.getLogger(__name__)
def add_transaction(transaction):
"""add a new transaction.
:param transaction: the new transaction object.
"""
logger.debug("Auditing record: [{}] ...start".format(transaction))
conn = factory.get_transaction_connection()
conn.add_record(transaction)
logger.debug("Auditing record...end")
def get_transactions(query, limit, marker):
"""get all transactions that meet the given query.
:param q: the query to use in order to search for relevant.
transactions.
:param limit: the maximun number of transactions to return.
:param marker: a place order for pagination (not implemented yet).
"""
logger.debug("Getting records for query:{}...start".format(query))
conn = factory.get_transaction_connection()
transactions = conn.get_records(query, limit, marker)
logger.debug("Getting records for query...end")
return QueryResultModel(transactions)

View File

@ -0,0 +1 @@
"""storage package."""

View File

@ -0,0 +1,12 @@
"""factory module."""
from audit_server.storage.mysql.transaction import Connection as Transaction
database_url = 'NA'
echo_statements = False
def get_transaction_connection():
"""return a transaction orm implementation."""
return Transaction(database_url, echo_statements)

View File

@ -0,0 +1 @@
"""mysql package."""

View File

@ -0,0 +1,168 @@
"""mysql transaction module."""
import logging
from sqlalchemy import Column, Integer, Text, BigInteger, asc
from sqlalchemy import create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative.api import declarative_base
from sqlalchemy.orm import sessionmaker
from audit_server.model.transaction import Model
from audit_server.storage import transaction
Base = declarative_base()
logger = logging.getLogger(__name__)
class Record(Base):
"""record base class."""
__tablename__ = 'transactions'
id = Column(Integer, autoincrement=True, primary_key=True)
timestamp = Column(BigInteger)
user_id = Column(Text)
application_id = Column(Text)
tracking_id = Column(Text)
external_id = Column(Text)
transaction_id = Column(Text)
transaction_type = Column(Text)
event_details = Column(Text)
resource_id = Column(Text)
service_name = Column(Text)
class Connection(transaction.Base):
"""Implements mysql DB."""
def __init__(self, url, echo_statements):
"""init method."""
self._engine = create_engine(url, echo=echo_statements)
self._session_maker = sessionmaker(bind=self._engine)
pass
def add_record(self, transaction_record):
"""add a new transaction record.
:param transaction_record: the new transaction record.
"""
logger.debug(
"Auditing record: [{}] ...start".format(transaction_record))
try:
session = self._session_maker()
session.add(Record(timestamp=transaction_record.timestamp,
user_id=transaction_record.user_id,
application_id=transaction_record.
application_id,
tracking_id=transaction_record.tracking_id,
external_id=transaction_record.external_id,
transaction_id=transaction_record.
transaction_id,
transaction_type=transaction_record.
transaction_type,
event_details=transaction_record.event_details,
resource_id=transaction_record.resource_id,
service_name=transaction_record.service_name))
session.commit()
# All other exceptions will be raised
except IntegrityError as e:
# Except Exception as e:
session.rollback()
# Raise the exception only if it's not a duplicate entry exception
if 'duplicate entry' in e.message.lower():
logger.warning(
"Fail to audit record - Duplicate entry: {}".format(
e))
else:
logger.warning("Fail to audit record: {}".format(e))
raise e
finally:
session.close()
logger.debug("Auditing record...end")
def get_records(self, query, limit=10, marker=None):
"""get all records records that meet the given query.
:param q: the query to use in order to search for relevant.
records.
:param limit: the maximun number of records to return.
:param marker: a place order for pagination (not implemented yet).
"""
logger.debug("Getting records using query:[{}]...start".format(query))
try:
session = self._session_maker()
records = session.query(Record)
if query is not None:
records = Connection._add_filter(records, query, marker)
records = records.order_by(asc(Record.timestamp))
records = records.limit(limit)
records_result = records.all()
transactions = []
for record in records_result:
timestamp = record.timestamp
user_id = record.user_id
application_id = record.application_id
tracking_id = record.tracking_id
external_id = record.external_id
transaction_id = record.transaction_id
transaction_type = record.transaction_type
event_details = record.event_details
resource_id = record.resource_id
service_name = record.service_name
model = Model(timestamp, user_id, application_id, tracking_id,
external_id, transaction_id, transaction_type,
event_details, resource_id, service_name)
transactions.append(model)
logger.debug(
"Getting records using query:[{}] "
"return the result :[{}]...start".format(
query, transactions))
logger.debug("Getting records using query...end")
return transactions
finally:
session.close()
@staticmethod
def _add_filter(records, query, marker):
"""add filter to the select based on the given query.
:param records: the records created so far.
:param query: the query to use in order to search for relevant.
records.
:param marker: a place order for pagination (not implemented yet).
"""
logger.debug("add_filter for query: [{}] ...start".format(query))
if marker is not None:
records = records.filter(Record.timestamp > marker)
if query.timestamp_from is not None:
records = records.filter(Record.timestamp >= query.timestamp_from)
if query.timestamp_to is not None:
records = records.filter(Record.timestamp <= query.timestamp_to)
records = Connection._add_filter_eq(records, Record.user_id,
query.user_id)
records = Connection._add_filter_eq(records, Record.application_id,
query.application_id)
records = Connection._add_filter_eq(records, Record.tracking_id,
query.tracking_id)
records = Connection._add_filter_eq(records, Record.external_id,
query.external_id)
records = Connection._add_filter_eq(records, Record.transaction_id,
query.transaction_id)
records = Connection._add_filter_eq(records, Record.transaction_type,
query.transaction_type)
records = Connection._add_filter_eq(records, Record.event_details,
query.event_details)
records = Connection._add_filter_eq(records, Record.resource_id,
query.resource_id)
records = Connection._add_filter_eq(records, Record.service_name,
query.service_name)
logger.debug("add_filter for query: [{}] ...end".format(query))
return records
@staticmethod
def _add_filter_eq(records, key, value):
if value is not None:
records = records.filter(key == value)
return records

View File

@ -0,0 +1,17 @@
"""transaction interface module."""
class Base(object):
"""transaction base class."""
def __init__(self, url):
"""init method."""
pass
def add_record(self, transaction):
"""add new transaction record to the db."""
raise NotImplementedError("Please Implement this method")
def get_records(self, query):
"""get transactions that meet the given query from the db."""
raise NotImplementedError("Please Implement this method")

View File

@ -0,0 +1,12 @@
<%inherit file="layout.html" />
## provide definitions for blocks we want to redefine
<%def name="title()">
Server Error ${status}
</%def>
## now define the body of the template
<header>
<h1>Server Error ${status}</h1>
</header>
<p>${message}</p>

View File

@ -0,0 +1,34 @@
<%inherit file="layout.html" />
## provide definitions for blocks we want to redefine
<%def name="title()">
Welcome to Pecan!
</%def>
## now define the body of the template
<header>
<h1><img src="/images/logo.png" /></h1>
</header>
<div id="content">
<p>This is a sample Pecan project.</p>
<p>
Instructions for getting started can be found online at <a
href="http://pecanpy.org" target="window">pecanpy.org</a>
</p>
<p>
...or you can search the documentation here:
</p>
<form method="POST" action="/">
<fieldset>
<input name="q" />
<input type="submit" value="Search" />
</fieldset>
<small>Enter search terms or a module, class or function name.</small>
</form>
</div>

View File

@ -0,0 +1,22 @@
<html>
<head>
<title>${self.title()}</title>
${self.style()}
${self.javascript()}
</head>
<body>
${self.body()}
</body>
</html>
<%def name="title()">
Default Title
</%def>
<%def name="style()">
<link rel="stylesheet" type="text/css" media="screen" href="/css/style.css" />
</%def>
<%def name="javascript()">
<script language="text/javascript" src="/javascript/shared.js"></script>
</%def>

View File

@ -0,0 +1 @@
"""test package."""

View File

@ -0,0 +1 @@
"""controllers package."""

View File

@ -0,0 +1,154 @@
"""Base classes for API tests."""
import pecan
import pecan.testing
import unittest
class FunctionalTest(unittest.TestCase):
"""Used for functional tests of Pecan controllers.
Used in case when you need to test your literal application and its
integration with the framework.
"""
PATH_PREFIX = ''
def setUp(self):
"""set up method."""
self.app = self._make_app()
def _make_app(self, enable_acl=False):
"""make app method."""
self.config = {
'app': {
'root': 'audit_server.controllers.root.RootController',
'modules': ['audit_server']
},
'wsme': {
'debug': True,
},
'database': {
'url': 'mysql://dummy:dummy@1.1.1.1/orm_audit?charset=utf8',
'echo_statements': False
}
}
return pecan.testing.load_test_app(self.config)
def tearDown(self):
"""tear down method."""
super(FunctionalTest, self).tearDown()
pecan.set_config({}, overwrite=True)
def put_json(self, path, params, expect_errors=False, headers=None,
extra_environ=None, status=None):
"""Send simulated HTTP PUT request to Pecan test app.
:param path: url path of target service
:param params: content for wsgi.input of request
:param expect_errors: boolean value whether an error is expected based
on request
:param headers: A dictionary of headers to send along with the request
:param extra_environ: A dictionary of environ variables to send along
with the request
:param status: Expected status code of response
"""
return self.post_json(path=path, params=params,
expect_errors=expect_errors,
headers=headers, extra_environ=extra_environ,
status=status, method="put")
def post_json(self, path, params, expect_errors=False, headers=None,
method="post", extra_environ=None, status=None):
"""Send simulated HTTP POST request to Pecan test app.
:param path: url path of target service
:param params: content for wsgi.input of request
:param expect_errors: boolean value whether an error is expected based
on request
:param headers: A dictionary of headers to send along with the request
:param method: Request method type. Appropriate method function call
should be used rather than passing attribute in.
:param extra_environ: A dictionary of environ variables to send along
with the request
:param status: Expected status code of response
"""
full_path = self.PATH_PREFIX + path
response = getattr(self.app, "%s_json" % method)(
str(full_path),
params=params,
headers=headers,
status=status,
extra_environ=extra_environ,
expect_errors=expect_errors
)
return response
def delete(self, path, expect_errors=False, headers=None,
extra_environ=None, status=None):
"""Send simulated HTTP DELETE request to Pecan test app.
:param path: url path of target service
:param expect_errors: boolean value whether an error is expected based
on request
:param headers: A dictionary of headers to send along with the request
:param extra_environ: A dictionary of environ variables to send along
with the request
:param status: Expected status code of response
"""
full_path = self.PATH_PREFIX + path
response = self.app.delete(str(full_path),
headers=headers,
status=status,
extra_environ=extra_environ,
expect_errors=expect_errors)
return response
def get_json(self, path, expect_errors=False, headers=None,
extra_environ=None, q=None, groupby=None, status=None,
override_params=None, **params):
"""Send simulated HTTP GET request to Pecan test app.
:param path: url path of target service
:param expect_errors: boolean value whether an error is expected based
on request
:param headers: A dictionary of headers to send along with the request
:param extra_environ: A dictionary of environ variables to send along
with the request
:param q: list of queries consisting of: field, value, op, and type
keys
:param groupby: list of fields to group by
:param status: Expected status code of response
:param override_params: literally encoded query param string
:param params: content for wsgi.input of request
"""
q = q or []
groupby = groupby or []
full_path = self.PATH_PREFIX + path
if override_params:
all_params = override_params
else:
query_params = {'q.field': [],
'q.value': [],
'q.op': [],
'q.type': [],
}
for query in q:
for name in ['field', 'op', 'value', 'type']:
query_params['q.%s' % name].append(query.get(name, ''))
all_params = {}
all_params.update(params)
if q:
all_params.update(query_params)
if groupby:
all_params.update({'groupby': groupby})
response = self.app.get(full_path,
params=all_params,
headers=headers,
extra_environ=extra_environ,
expect_errors=expect_errors,
status=status)
if not expect_errors:
response = response.json
return response

View File

@ -0,0 +1 @@
"""v1 package."""

View File

@ -0,0 +1,10 @@
"""functional_test module."""
from audit_server.tests.controllers.functional_test import FunctionalTest
class FunctionalTest(FunctionalTest):
"""base functional test class."""
PATH_PREFIX = '/v1'

View File

@ -0,0 +1,38 @@
"""test_base module."""
from audit_server.controllers.v1.base import ClientSideError
from audit_server.controllers.v1.base import EntityNotFoundError
from audit_server.controllers.v1.base import InputValueError
import unittest
class Test(unittest.TestCase):
"""test case class."""
def test_init_ClientSideError(self):
"""test the init method."""
expected_msg = "This is an error"
expected_code = 400
error = ClientSideError(expected_msg)
self.assertEqual(error.msg, expected_msg)
self.assertEqual(error.code, expected_code)
def test_init_InputValueError(self):
"""test the init method."""
name = "name1"
value = "value1"
expected_msg = "Invalid value for input {} : {}".format(name, value)
expected_code = 400
error = InputValueError(name, value)
self.assertEqual(error.msg, expected_msg)
self.assertEqual(error.code, expected_code)
def test_init_EntityNotFoundError(self):
"""test the init method."""
id = "id1"
expected_msg = "Entity not found for {}".format(id)
expected_code = 404
error = EntityNotFoundError(id)
self.assertEqual(error.msg, expected_msg)
self.assertEqual(error.code, expected_code)

View File

@ -0,0 +1,14 @@
"""Get configuration module unittests."""
from audit_server.controllers.v1 import configuration as root
from audit_server.tests.controllers.v1.functional_test import FunctionalTest
from mock import patch
class TestGetConfiguration(FunctionalTest):
"""Main get configuration test case."""
@patch.object(root.utils, 'report_config', return_value='12345')
def test_get_configuration_success(self, input):
"""Test get_configuration returns the expected value on success."""
response = self.app.get('/v1/audit/configuration')
self.assertEqual(response.json, '12345')

View File

@ -0,0 +1,25 @@
"""Logs module unittests."""
from audit_server.tests.controllers.v1.functional_test import FunctionalTest
class TestLogs(FunctionalTest):
"""logs tests."""
def test_change_log_level_fail(self):
response = self.app.put('/v1/audit/logs/1')
expected_result = {
"result": "Fail to change log_level. Reason: "
"The given log level [1] doesn't exist."}
self.assertEqual(expected_result, response.json)
def test_change_log_level_none(self):
response = self.app.put('/v1/audit/logs', expect_errors=True)
expected_result = 'Missing argument: "level"'
self.assertEqual(response.json["faultstring"], expected_result)
self.assertEqual(response.status_code, 400)
def test_change_log_level_success(self):
response = self.app.put('/v1/audit/logs/debug')
expected_result = {'result': 'Log level changed to debug.'}
self.assertEqual(response.json, expected_result)
self.assertEqual(response.status_code, 201)

View File

@ -0,0 +1,97 @@
"""test_transaction module."""
from mock import patch
from audit_server.controllers.v1.transaction import QueryResult
from audit_server.model.transaction import Model as TransactionModel
from audit_server.model.transaction_query_result import \
Model as TransactionQueryResultModel
from audit_server.services import transaction as transaction_service
from audit_server.tests.controllers.v1.functional_test import FunctionalTest
class Test(FunctionalTest):
"""test transaction class."""
transaction_model = TransactionModel(timestamp=111, user_id="user_id_1",
application_id="application_id_1",
tracking_id="tracking_id_1",
external_id="external_id_1",
transaction_id="transaction_id_1",
transaction_type="transaction_type_1",
event_details="event_details_1",
resource_id="resource_id_1",
service_name="service_name_1")
@patch.object(transaction_service, 'get_transactions',
return_value=TransactionQueryResultModel(
[transaction_model]))
def test_get_all(self, mock_get_transactions):
"""test that get_one returns an appropriate json result."""
url = "/audit/transaction?q.timestamp_from=1111&q.timestamp_to=5555&" \
"q.user_id=user1&q.application_id=SSP&limit=15&marker=1"
output = self.get_json(url)
returned_transactions = output['transactions']
self.assertIsNotNone(returned_transactions)
self.assertEqual(len(returned_transactions), 1)
transaction = returned_transactions[0]
self._assert_returned_transaction(transaction)
@patch.object(transaction_service, 'get_transactions',
return_value=TransactionQueryResultModel(
[transaction_model]))
def test_get_all_with_empty_query(self, mock_get_transactions):
"""Test that get_one accepts an empty query and returns a response."""
url = "/audit/transaction?limit=15&marker=1"
output = self.get_json(url)
returned_transactions = output['transactions']
self.assertIsNotNone(returned_transactions)
self.assertEqual(len(returned_transactions), 1)
transaction = returned_transactions[0]
self._assert_returned_transaction(transaction)
@patch.object(transaction_service, 'add_transaction')
def test_post(self, mock_transaction_service):
"""Test that post is executed with no exceptions."""
url = "/audit/transaction"
body = {
"timestamp": 111,
"user_id": "user_1",
"application_id": "application_id_1",
"tracking_id": "tracking_id_1",
"external_id": "external_id_1",
"transaction_id": "transaction_id_1",
"transaction_type": "transaction_type_1",
"event_details": "event_details_1",
"resource_id": "resource_id_1",
"service_name": "service_name_1"
}
self.post_json(url, body)
def test_init_QueryResult(self):
"""test the init method."""
QueryResult(**{"prop1": "value1", "prop2": "value2"})
def _assert_returned_transaction(self, transaction):
"""Check the returned trasaction."""
self.assertEqual(transaction['timestamp'],
self.transaction_model.timestamp)
self.assertEqual(transaction['user_id'],
self.transaction_model.user_id)
self.assertEqual(transaction['application_id'],
self.transaction_model.application_id)
self.assertEqual(transaction['tracking_id'],
self.transaction_model.tracking_id)
self.assertEqual(transaction['external_id'],
self.transaction_model.external_id)
self.assertEqual(transaction['transaction_id'],
self.transaction_model.transaction_id)
self.assertEqual(transaction['transaction_type'],
self.transaction_model.transaction_type)
self.assertEqual(transaction['event_details'],
self.transaction_model.event_details)
self.assertEqual(transaction['resource_id'],
self.transaction_model.resource_id)
self.assertEqual(transaction['service_name'],
self.transaction_model.service_name)

View File

@ -0,0 +1 @@
"""model package."""

View File

@ -0,0 +1,19 @@
"""test_transaction_query_result module."""
import unittest
from audit_server.model.transaction_query_result import \
Model as TransactionQueryResultModel
class Test(unittest.TestCase):
"""test transaction query result class."""
def test_str(self):
"""test str method."""
transactions = []
model = TransactionQueryResultModel(transactions)
self.assertEqual(str(model),
"TransactionQueryResult:[ transactions={}]".format(
transactions))

View File

@ -0,0 +1 @@
"""services package."""

View File

@ -0,0 +1,14 @@
"""test_base module."""
from audit_server.services.base import Error
import unittest
class Test(unittest.TestCase):
"""test base class."""
def test_init_Error(self):
"""Test that init of Error succeeded."""
Error("test")
pass

View File

@ -0,0 +1,56 @@
"""test_transaction module."""
import unittest
from mock import patch
from audit_server.model.transaction import Model as TransactionModel
from audit_server.model.transaction_query import Model as TransactionQuery
from audit_server.services import transaction as TransactionService
from audit_server.storage import factory
class Test(unittest.TestCase):
"""test transaction class."""
@patch.object(factory, 'get_transaction_connection')
def test_add_transaction(self, mock_factory):
"""test that add_transaction doesn't throws any exception."""
timestamp = 111
user_id = "user_id_1"
application_id = "application_id_1"
tracking_id = "tracking_id_1"
external_id = "external_id_1"
transaction_id = "transaction_id_1"
transaction_type = "transaction_type_1"
event_details = "event_details_1"
resource_id = "resource_id_1"
service_name = "service_name_1"
transaction = TransactionModel(timestamp, user_id, application_id,
tracking_id, external_id,
transaction_id, transaction_type,
event_details, resource_id,
service_name)
TransactionService.add_transaction(transaction)
@patch.object(factory, 'get_transaction_connection')
def test_get_transaction(query, mock_factory):
"""test that get_transaction doesn't throws any exception."""
timestamp_from = 111
timestamp_to = 555
user_id = "user_id_1"
application_id = "application_id_1"
tracking_id = "tracking_id_1"
external_id = "external_id_1"
transaction_id = "transaction_id_1"
transaction_type = "transaction_type_1"
event_details = "event_details_1"
resource_id = "resource_id_1"
service_name = "service_name_1"
query = TransactionQuery(timestamp_from, timestamp_to, user_id,
application_id, tracking_id, external_id,
transaction_id, transaction_type,
event_details, resource_id, service_name)
limit = 10
marker = 1
TransactionService.get_transactions(query, limit, marker)

View File

@ -0,0 +1 @@
"""storage package."""

View File

@ -0,0 +1 @@
"""mysql package."""

View File

@ -0,0 +1,226 @@
"""test_transaction module."""
import unittest
from mock import patch
from sqlalchemy import create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import sessionmaker
from audit_server.model.transaction import Model as TransactionModel
from audit_server.model.transaction_query import Model as TransactionQueryModel
from audit_server.storage.mysql.transaction import Connection
from audit_server.storage.mysql.transaction import Record
class Test(unittest.TestCase):
"""test transaction class."""
transaction = TransactionModel(timestamp=111, user_id="user_id_1",
application_id="application_id_1",
tracking_id="tracking_id_1",
external_id="external_id_1",
transaction_id="transaction_id_1",
transaction_type="transaction_type_1",
event_details="event_details_1",
resource_id="resource_id_1",
service_name="service_name_1")
transaction_query = TransactionQueryModel(111, 222, "user_id1",
"application_id1",
"tracking_id1",
"external_id1",
"transaction_id1",
"transaction_type1",
"event_details1",
"resource_id_1",
"service_name_1")
@patch.object(create_engine, '__init__')
@patch.object(sessionmaker, '__init__')
def setUp(self, mock_engine, mock_sessionmaker):
"""setup method."""
self.mock_engine = mock_engine
self.mock_sessionmaker = mock_sessionmaker
self.conn = object.__new__(Connection)
self.conn._engine = self.mock_engine
self.conn._session_maker = self.mock_sessionmaker
def test_add_record(self):
"""test add record.
test that no exception/error is thrown when calling the
add_record execution.
"""
self.conn.add_record(self.transaction)
def test_add_record_duplicate_entry(self):
"""test duplicate entries to add record.
test that when session.add throws Duplicate Entry exception,
no expcetion is thrown back.
"""
side_effect = IntegrityError(None, None, None, None)
side_effect.message = "Duplicate entry"
mock_session = self.mock_sessionmaker.return_value.__call__
mock_add = mock_session.im_self.add
mock_add.side_effect = side_effect
self.conn.add_record(self.transaction)
def test_add_record_integrity_entry(self):
"""test add record that throws integrity error.
test that when session.add throws a different exception
than Duplicate Entry it will be rasied up.
"""
side_effect = IntegrityError(None, None, None, None)
side_effect.message = "Some other integrity error"
mock_session = self.mock_sessionmaker.return_value.__call__
mock_add = mock_session.im_self.add
mock_add.side_effect = IntegrityError("Some other integrity error",
None, None, None)
self.assertRaises(IntegrityError, self.conn.add_record,
self.transaction)
def test_get_records(self):
"""test get records.
test that get_records returns a list and that
no exception/error is thrown.
"""
# mock record
record = Record()
record.id = 1
record.timestamp = 111
record.user_id = "user_id1"
record.application_id = "application_id1"
record.tracking_id = "tracking_id1"
record.external_id = "external_id1"
record.transaction_id = "transaction_id1"
record.transaction_type = "transaction_type1"
record.event_details = "event_details1"
mock_returned_records = [record]
# The transaction query above will result in the following method call:
# EngineFacade().get_session().query().filter().filter().filter().
# filter().filter().filter().filter().filter().filter().filter().
# order_by().limit() Therefore we will need to mock the entire
# call tree in order to set the required returned value
mock_session = self.mock_sessionmaker.return_value.__call__
mock_query = mock_session.im_self.query
mock_filter = mock_query.return_value.filter
for key in range(len(self.transaction_query.__dict__.keys()) - 1):
mock_filter = mock_filter.return_value.filter
mock_order_by = mock_filter.return_value.order_by
mock_limit = mock_order_by.return_value.limit
mock_all = mock_limit.return_value.all
mock_all.return_value = mock_returned_records
returned_records = self.conn.get_records(self.transaction_query)
self.assertIsNotNone(returned_records)
self.assertIsInstance(returned_records, list)
# Check that the list is not empty
self.assertTrue(returned_records)
# Compare the return record value with the expected value
self.assertTrue(len(returned_records), len(mock_returned_records))
self.assertEqual(returned_records[0].timestamp,
mock_returned_records[0].timestamp)
self.assertEqual(returned_records[0].user_id,
mock_returned_records[0].user_id)
self.assertEqual(returned_records[0].application_id,
mock_returned_records[0].application_id)
self.assertEqual(returned_records[0].tracking_id,
mock_returned_records[0].tracking_id)
self.assertEqual(returned_records[0].external_id,
mock_returned_records[0].external_id)
self.assertEqual(returned_records[0].transaction_id,
mock_returned_records[0].transaction_id)
self.assertEqual(returned_records[0].transaction_type,
mock_returned_records[0].transaction_type)
self.assertEqual(returned_records[0].event_details,
mock_returned_records[0].event_details)
def test_get_records_returns_none(self):
"""test get records return none.
Test get_records will return empty list when session
returns empty list.
"""
mock_session = self.mock_engine.return_value.get_session
mock_query = mock_session.return_value.query
mock_filter_by = mock_query.return_value.filter_by
mock_order_by = mock_filter_by.return_value.order_by
mock_first = mock_order_by.return_value.first
mock_first.return_value = []
returned_records = self.conn.get_records(self.transaction_query)
self.assertIsNotNone(returned_records)
self.assertIsInstance(returned_records, list)
# Check that the list is empty
self.assertTrue(not returned_records)
def test_get_records_with_empty_query(self):
"""test get record with empty query.
test get_records with empty query will not throw an exception
and will returns a result.
"""
transaction_query = None
self.conn.get_records(transaction_query)
def test_add_filter(self):
"""test that add_filter is executed with no exceptions/error."""
marker = 5
session = self.mock_engine.get_session()
records = session.query(Record)
records = self.conn._add_filter(records, self.transaction_query,
marker)
def test_add_filter_with_timestamp_empty(self):
"""test add filter with empty timestamp.
test that add_filter is executed with no exceptions/error
when timestamp_from and timestamp_to are empty.
"""
timestamp_from = None
timestamp_to = None
user_id = "user_id_1"
application_id = "application_id_1"
tracking_id = "tracking_id_1"
external_id = "external_id_1"
transaction_id = "transaction_id_1"
transaction_type = "transaction_type_1"
event_details = "event_details_1"
resource_id = "resource_id_1"
service_name = "service_name_1"
query = TransactionQueryModel(timestamp_from, timestamp_to, user_id,
application_id, tracking_id,
external_id, transaction_id,
transaction_type, event_details,
resource_id, service_name)
marker = 5
session = self.mock_engine.get_session()
records = session.query(Record)
records = self.conn._add_filter(records, query, marker)
def test_add_filter_eq(self):
"""test that add_filter_eq will not throw an exception."""
session = self.mock_engine.get_session()
records = session.query(Record)
key = "key1"
value = "value1"
records = self.conn._add_filter_eq(records, key, value)
def test_add_filter_eq_with_empty_value(self):
"""test add filter with empty value.
test that add_filter_eq will not throw an exception
when value is None.
"""
session = self.mock_engine.get_session()
records = session.query(Record)
key = "key1"
value = None
records = self.conn._add_filter_eq(records, key, value)

View File

@ -0,0 +1,25 @@
"""test_factory module."""
from audit_server.storage import factory
from audit_server.storage.mysql.transaction import Connection
from mock import patch
from sqlalchemy import create_engine
import unittest
class Test(unittest.TestCase):
"""test factory class."""
@patch.object(create_engine, '__init__')
def test_get_get_transaction_connection(self, mock_engine):
"""test get_zone_resource_type_status.
test that get_zone_resource_type_status_connection returns
an instance of type ZoneResourceTypeStatusConnection.
"""
factory.database_url = 'mysql://root:stack@127.0.0.1/orm_audit?' \
'charset=utf8'
factory.echo_statements = False
mock_engine.get_session.return_value = None
conn = factory.get_transaction_connection()
self.assertIsInstance(conn, Connection)

View File

@ -0,0 +1,21 @@
"""test_transaction module."""
from audit_server.storage.transaction import Base
import unittest
class Test(unittest.TestCase):
"""test transaction class."""
def test_add_record(self):
"""test that add_record throws an NotImplementedError exception."""
baseConn = Base("test_url")
self.assertRaises(NotImplementedError, baseConn.add_record,
transaction=None)
def test_get_latest_record(self):
"""test that add_record throws an NotImplementedError exception."""
baseConn = Base("test_url")
self.assertRaises(NotImplementedError, baseConn.get_records,
query=None)

View File

@ -0,0 +1,175 @@
# This is the Audit Server API
swagger: '2.0'
info:
title: Audit Server API
description: Audit Server service
version: "1.0.0"
# the domain of the service
host: server
# array of all schemes that your API supports
schemes:
- http
# will be prefixed to all paths
basePath: /v1
produces:
- application/json
paths:
/audit/transaction:
get:
summary: get audit transactions
description: |
Returns transactions that meet the query.
parameters:
- name: q.timestamp_from
in: query
description: Transaction from timestamp.
required: false
type: number
format: long
- name: q.timestamp_to
in: query
description: Transaction to timestamp.
required: false
type: number
format: long
- name: q.user_id
in: query
description: Transaction user id.
required: false
type: string
- name: q.application_id
in: query
description: Transaction application id.
required: false
type: string
- name: q.tracking_id
in: query
description: Transaction tracking id.
required: false
type: string
- name: q.external_id
in: query
description: Transaction external id.
required: false
type: string
- name: q.transaction_id
in: query
description: Transaction transaction id.
required: false
type: string
- name: q.transaction_type
in: query
description: Transaction transaction type.
required: false
type: string
- name: q.event_details
in: query
description: Transaction event details.
required: false
type: string
- name: q.status
in: query
description: Transaction status.
required: false
type: string
- name: q.resource_id
in: query
description: Transaction resource id.
required: false
type: string
- name: q.service_name
in: query
description: Transaction service name.
required: false
type: string
- name: limit
in: query
description: Max number of records to return
required: false
type: string
#tags:
# - Transactions
responses:
200:
description: An array of transactions
schema:
$ref: '#/definitions/Transactions'
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
post:
summary: put audit transactions
description: |
Insert audit transactions
parameters:
- name: transaction
in: body
description: The audit transaction to add
required: true
schema:
$ref: "#/definitions/Transaction"
#tags:
# - Transactions
responses:
"201":
description: Null response
default:
description: unexpected error
schema:
$ref: '#/definitions/Error'
definitions:
Transactions:
type: object
properties:
transactions:
type: array
items:
$ref: '#/definitions/Transaction'
Transaction:
type: object
properties:
timestamp:
type: number
format: long
description: Time of audit record in milliseconds since 1/1/1970.
user_id:
type: string
description: The concrete user which initiated this transaction (i.e the SSP user for which a create action was initiated).
application_id:
type: string
description: (SSP, eCOMP, etc).
tracking_id:
type: string
description: The “session” identifier for a set of transactions, and will be the same all through the flow. So if SSP calls CMS with its own transaction id, it should also be used as the tracking id. If it does not include some identifier, the CMS generated transaction id can serve as the tracking id. Either way, that tracking id will be preserved and identify all actions along the flow on RDS, by the ORD, etc.
external_id:
type: string
description: Anything we get from outside the ORM. For example, if the SSP has its own transaction ID, it may forward it to us.
transaction_id:
type: string
description: The transaction ID of the component creating the current record.
transaction_type:
type: string
description: The type of transaction (depends on the application business logic).
event_details:
type: string
description: Free description, may be transaction type specific data.
status:
type: string
description: The transaction status (depends on the application business logic).
resource_id:
type: string
description: The UUID of the resource on which this action is operating.
service_name:
type: string
description: The service which initiated the log ie- CMS, RDS, RMS....
Error:
type: object
properties:
faultcode:
type: string
faultstring:
type: string
debuginfo:
type: string

View File

@ -0,0 +1,24 @@
# Swagger generated server
## Overview
This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub. This is an example of building a node.js server.
This example uses the [expressjs](http://expressjs.com/) framework. To see how to make this your own, look here:
[README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md)
### Running the server
To run the server, follow these simple steps:
```
npm install
node .
```
To view the Swagger UI interface:
```
open http://localhost:8080/docs
```
This project leverages the mega-awesome [swagger-tools](https://github.com/apigee-127/swagger-tools) middleware which does most all the work.

View File

@ -0,0 +1,178 @@
---
swagger: "2.0"
info:
description: "Audit Server service"
version: "1.0.0"
title: "Audit Server API"
host: "server"
basePath: "/v1"
schemes:
- "http"
produces:
- "application/json"
paths:
/audit/transaction:
get:
summary: "get audit transactions"
description: "Returns transactions that meet the query.\n"
operationId: "auditTransactionGET"
parameters:
- name: "q.timestamp_from"
in: "query"
description: "Transaction from timestamp."
required: false
type: "number"
format: "long"
- name: "q.timestamp_to"
in: "query"
description: "Transaction to timestamp."
required: false
type: "number"
format: "long"
- name: "q.user_id"
in: "query"
description: "Transaction user id."
required: false
type: "string"
- name: "q.application_id"
in: "query"
description: "Transaction application id."
required: false
type: "string"
- name: "q.tracking_id"
in: "query"
description: "Transaction tracking id."
required: false
type: "string"
- name: "q.external_id"
in: "query"
description: "Transaction external id."
required: false
type: "string"
- name: "q.transaction_id"
in: "query"
description: "Transaction transaction id."
required: false
type: "string"
- name: "q.transaction_type"
in: "query"
description: "Transaction transaction type."
required: false
type: "string"
- name: "q.event_details"
in: "query"
description: "Transaction event details."
required: false
type: "string"
- name: "q.status"
in: "query"
description: "Transaction status."
required: false
type: "string"
- name: "q.resource_id"
in: "query"
description: "Transaction resource id."
required: false
type: "string"
- name: "q.service_name"
in: "query"
description: "Transaction service name."
required: false
type: "string"
- name: "limit"
in: "query"
description: "Max number of records to return"
required: false
type: "string"
responses:
200:
description: "An array of transactions"
schema:
$ref: "#/definitions/Transactions"
default:
description: "Unexpected error"
schema:
$ref: "#/definitions/Error"
x-swagger-router-controller: "Default"
post:
summary: "put audit transactions"
description: "Insert audit transactions\n"
operationId: "auditTransactionPOST"
parameters:
- in: "body"
name: "transaction"
description: "The audit transaction to add"
required: true
schema:
$ref: "#/definitions/Transaction"
responses:
201:
description: "Null response"
default:
description: "unexpected error"
schema:
$ref: "#/definitions/Error"
x-swagger-router-controller: "Default"
definitions:
Transactions:
type: "object"
properties:
transactions:
type: "array"
items:
$ref: "#/definitions/Transaction"
Transaction:
type: "object"
properties:
timestamp:
type: "number"
format: "long"
description: "Time of audit record in milliseconds since 1/1/1970."
user_id:
type: "string"
description: "The concrete user which initiated this transaction (i.e the\
\ SSP user for which a create action was initiated)."
application_id:
type: "string"
description: "(SSP, eCOMP, etc)."
tracking_id:
type: "string"
description: "The “session” identifier for a set of transactions, and will\
\ be the same all through the flow. So if SSP calls CMS with its own transaction\
\ id, it should also be used as the tracking id. If it does not include\
\ some identifier, the CMS generated transaction id can serve as the tracking\
\ id. Either way, that tracking id will be preserved and identify all actions\
\ along the flow on RDS, by the ORD, etc."
external_id:
type: "string"
description: "Anything we get from outside the ORM. For example, if the SSP\
\ has its own transaction ID, it may forward it to us."
transaction_id:
type: "string"
description: "The transaction ID of the component creating the current record."
transaction_type:
type: "string"
description: "The type of transaction (depends on the application business\
\ logic)."
event_details:
type: "string"
description: "Free description, may be transaction type specific data."
status:
type: "string"
description: "The transaction status (depends on the application business\
\ logic)."
resource_id:
type: "string"
description: "The UUID of the resource on which this action is operating."
service_name:
type: "string"
description: "The service which initiated the log ie- CMS, RDS, RMS...."
Error:
type: "object"
properties:
faultcode:
type: "string"
faultstring:
type: "string"
debuginfo:
type: "string"

View File

@ -0,0 +1,15 @@
'use strict';
var url = require('url');
var Default = require('./DefaultService');
module.exports.auditTransactionGET = function auditTransactionGET (req, res, next) {
Default.auditTransactionGET(req.swagger.params, res, next);
};
module.exports.auditTransactionPOST = function auditTransactionPOST (req, res, next) {
Default.auditTransactionPOST(req.swagger.params, res, next);
};

View File

@ -0,0 +1,60 @@
'use strict';
exports.auditTransactionGET = function(args, res, next) {
/**
* parameters expected in the args:
* q.timestampFrom (BigDecimal)
* q.timestampTo (BigDecimal)
* q.userId (String)
* q.applicationId (String)
* q.trackingId (String)
* q.externalId (String)
* q.transactionId (String)
* q.transactionType (String)
* q.eventDetails (String)
* q.status (String)
* q.resourceId (String)
* q.serviceName (String)
* limit (String)
**/
var examples = {};
examples['application/json'] = {
"transactions" : [ {
"transaction_id" : "aeiou",
"user_id" : "aeiou",
"service_name" : "aeiou",
"resource_id" : "aeiou",
"external_id" : "aeiou",
"event_details" : "aeiou",
"transaction_type" : "aeiou",
"application_id" : "aeiou",
"tracking_id" : "aeiou",
"timestamp" : 1.3579000000000001069366817318950779736042022705078125,
"status" : "aeiou"
} ]
};
if(Object.keys(examples).length > 0) {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(examples[Object.keys(examples)[0]] || {}, null, 2));
}
else {
res.end();
}
}
exports.auditTransactionPOST = function(args, res, next) {
/**
* parameters expected in the args:
* transaction (Transaction)
**/
// no response value expected for this operation
res.end();
}

View File

@ -0,0 +1,40 @@
'use strict';
var app = require('connect')();
var http = require('http');
var swaggerTools = require('swagger-tools');
var jsyaml = require('js-yaml');
var fs = require('fs');
var serverPort = 8080;
// swaggerRouter configuration
var options = {
swaggerUi: '/swagger.json',
controllers: './controllers',
useStubs: process.env.NODE_ENV === 'development' ? true : false // Conditionally turn on stubs (mock mode)
};
// The Swagger document (require it, build it programmatically, fetch it from a URL, ...)
var spec = fs.readFileSync('./api/swagger.yaml', 'utf8');
var swaggerDoc = jsyaml.safeLoad(spec);
// Initialize the Swagger middleware
swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) {
// Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain
app.use(middleware.swaggerMetadata());
// Validate Swagger requests
app.use(middleware.swaggerValidator());
// Route validated requests to appropriate controller
app.use(middleware.swaggerRouter(options));
// Serve the Swagger documents and Swagger UI
app.use(middleware.swaggerUi());
// Start the server
http.createServer(app).listen(serverPort, function () {
console.log('Your server is listening on port %d (http://localhost:%d)', serverPort, serverPort);
console.log('Swagger-ui is available on http://localhost:%d/docs', serverPort);
});
});

View File

@ -0,0 +1,16 @@
{
"name": "audit-server-api",
"version": "1.0.0",
"description": "Audit Server service",
"main": "index.js",
"keywords": [
"swagger"
],
"license": "MIT",
"private": true,
"dependencies": {
"connect": "^3.2.0",
"js-yaml": "^3.3.0",
"swagger-tools": "0.9.*"
}
}

Some files were not shown because too many files have changed in this diff Show More