Commit e4c9e308 authored by juga's avatar juga
Browse files

Refactor V3BwHeader to allow different spec versions

- Create constants for the separators
- Create methods to generate the header text
- Create method to parse the header text
- Fix tests
parent 394bcb5d
......@@ -9,6 +9,17 @@ from sbws.globals import SPEC_VERSION
log = logging.getLogger(__name__)
LINE_SEP = '\n'
K_SEP_V110 = '='
KV_SEP_V110 = '\n'
K_SEP_V200 = ' '
KV_SEP_V200 = ' '
ORDERED_KV = ['version', 'software', 'software_version']
ORDERED_K = ['timestamp', 'version', 'software', 'software_version']
ALLOWED_K = ORDERED_KV + ['earliest_bandwidth', 'generator_started']
TERMINATOR = '===='
LINE_TERMINATOR = TERMINATOR + LINE_SEP
class V3BwHeader(object):
"""
......@@ -20,6 +31,10 @@ class V3BwHeader(object):
:param str version: the spec version
:param str software: the name of the software that generates this
:param str software_version: the version of the software
:param dict kwargs: extra headers. Currently supported:
- earliest_bandwidth: str, ISO timestamp when the first bandwidth was
obtained
- generator_started: str, ISO timestamp when the generator started
"""
def __init__(self, timestamp=None, version=SPEC_VERSION, software='sbws',
software_version=__version__, **kwargs):
......@@ -30,14 +45,102 @@ class V3BwHeader(object):
if kwargs.get('earliest_bandwidth'):
self.earliest_bandwidth = kwargs['earliest_bandwidth']
if kwargs.get('generator_started'):
self.scanner_started = kwargs['generator_started']
self.generator_started = kwargs['generator_started']
@property
def kv_ordered_ls(self):
"""Return list of headers KeyValue tuples for the KeyValues
that have specific order.
"""
kv_ls = [(k, str(getattr(self, k, ''))) for k in ORDERED_KV]
log.debug('kv_ls %s', kv_ls)
return kv_ls
def __str__(self):
"""Return header string following spec version 1.1.0."""
@property
def k_extra_ls(self):
"""Return list of headers Keywords that do not have specific order."""
k_extra = list(set(self.__dict__.keys()).difference(ORDERED_K)
.intersection(ALLOWED_K))
log.debug('k_extra %s', k_extra)
return k_extra
@property
def kv_extra_ls(self):
"""Return list of headers KeyValue tuples for the KeyValues
that do not have specific order.
"""
# sorting the list to generate determinist headers
kv_headers = ' '.join(sorted(['='.join([k, str(v)])
for k, v in self.__dict__.items()]))
header = '\n'.join([str(self.timestamp), kv_headers, ''])
log.debug('header %s', header)
return header
kv_extra = sorted([(k, str(getattr(self, k)))
for k in self.k_extra_ls])
log.debug('kv_extra %s', kv_extra)
return kv_extra
@property
def kv_ls(self):
return self.kv_ordered_ls + self.kv_extra_ls
@property
def kv_v110_ls(self):
"""Return header kv list of strings following spec v1.1.0."""
kv = [str(self.timestamp)] + [K_SEP_V110.join([k, v])
for k, v in self.kv_ls]
log.debug('kv %s', kv)
return kv
def strv110(self):
"""Return header string following spec v1.1.0."""
header_str = LINE_SEP.join(self.kv_v110_ls) + LINE_SEP + \
LINE_TERMINATOR
log.debug('header_str %s', header_str)
return header_str
@property
def kv_v200_ls(self):
"""Return header kv following spec v2.0.0."""
kv = [str(self.timestamp)] + [K_SEP_V200.join([k, v])
for k, v in self.kv_ls]
log.debug('kv %s', kv)
return kv
@property
def strv200(self):
"""Return header string following spec v2.0.0."""
header_str = LINE_SEP.join(self.kv_v200_ls) + LINE_SEP + \
LINE_TERMINATOR
log.debug('header_str %s', header_str)
return header_str
@classmethod
def from_lines_v110(cls, lines):
"""
:param list lines: list of lines to parse
:returns: tuple of V3BwHeader object and non-header lines
"""
assert isinstance(lines, list)
try:
index_terminator = lines.index(TERMINATOR)
except ValueError as e:
# is not a bw file or is v100
log.warn('Terminator is not in lines')
return None
ts = int(lines[0])
# not checking order
kwargs = dict([l.split(K_SEP_V110)
for l in lines[:index_terminator]
if l.split(K_SEP_V110)[0] in ALLOWED_K])
h = cls(ts, **kwargs)
return h, lines[index_terminator + 1:]
@classmethod
def from_text_v110(self, text):
"""
:param list lines: text to parse
:returns: tuple of V3BwHeader object and non-header lines
"""
assert isinstance(text, str)
return self.from_lines_v110(text.split(LINE_SEP))
def __str__(self):
if self.version == '1.1.0':
return self.strv110()
return self.strv200
......@@ -7,7 +7,8 @@ import logging
log = logging.getLogger(__name__)
NUM_LINES_HEADER = 4
# TODO: this should be parsed from the results
NUM_LINES_HEADER = 6
def test_generate_no_dotsbws(tmpdir, caplog, parser):
......
# -*- coding: utf-8 -*-
"""Test generation of bandwidth measurements document (v3bw)"""
from sbws.lib.v3bwfile import V3BwHeader
from sbws.globals import SPEC_VERSION
from sbws.lib.v3bwfile import V3BwHeader, TERMINATOR, LINE_SEP, K_SEP_V110
from sbws import __version__ as version
timestamp = 1524661857
timestamp_l = str(timestamp)
version_l = K_SEP_V110.join(['version', SPEC_VERSION])
software_l = K_SEP_V110.join(['software', 'sbws'])
software_version_l = K_SEP_V110.join(['software_version', version])
header_ls = [timestamp_l, version_l, software_l, software_version_l,
TERMINATOR]
header_str = LINE_SEP.join(header_ls) + LINE_SEP
earliest_bandwidth = '2018-04-16T14:09:07'
earliest_bandwidth_l = K_SEP_V110.join(['earliest_bandwidth',
earliest_bandwidth])
generator_started = '2018-04-16T14:09:05'
generator_started_l = K_SEP_V110.join(['generator_started',
generator_started])
header_extra_ls = [timestamp_l, version_l, software_l, software_version_l,
earliest_bandwidth_l, generator_started_l, TERMINATOR]
header_extra_str = LINE_SEP.join(header_extra_ls) + LINE_SEP
def test_v3bwheader_str():
"""Test header str"""
timestamp = 1524661857
header = V3BwHeader(timestamp)
assert str(header) == '{}\nversion=1.1.0\nsoftware=sbws\n' \
'software_version={}\n'.format(timestamp, version)
def test_v3bwheader_earliest_bandwidth_str():
"""Test header str with earliest_bandwidth"""
timestamp = 1524661857
earliest_bandwidth = 1523887747.2904038
header = V3BwHeader(timestamp, earliest_bandwidth=earliest_bandwidth)
assert str(header) == '{}\nearliest_bandwidth=1523887747.2904038' \
'{}\nversion=1.1.0\nsoftware=sbws\n' \
'software_version={}\n'.format(timestamp, version)
def test_v3bwheader_scanner_started_str():
"""Test header str with scanner_started"""
timestamp = 1524661857
generator_started = 1523887747.2904038
header = V3BwHeader(timestamp, generator_started=generator_started)
assert str(header) == '{}\nscanner_started=1523887747.2904038' \
'{}\nversion=1.1.0\nsoftware=sbws\n' \
'software_version={}\n'.format(timestamp, version)
def test_v3bwheader_earliest_bandwidth_str():
"""Test header str with earliest_bandwidth"""
timestamp = 1524661857
earliest_bandwidth = 1523887747.2904038
header = V3BwHeader(timestamp, earliest_bandwidth=earliest_bandwidth)
assert str(header) == '{}\nearliest_bandwidth=1523887747.2904038 ' \
'software=sbws software_version=0.1.0 ' \
'timestamp=1524661857 version=1.1.0\n'.format(timestamp)
assert header_str == str(header)
def test_v3bwheader_extra_str():
"""Test header str with scanner_started and earliest_bandwidth"""
header = V3BwHeader(timestamp, generator_started=generator_started,
earliest_bandwidth=earliest_bandwidth)
assert header_extra_str == str(header)
def test_v3bwheader_from_lines():
""""""
header_obj = V3BwHeader(timestamp, generator_started=generator_started,
earliest_bandwidth=earliest_bandwidth)
header, _ = V3BwHeader().from_lines_v110(header_extra_ls)
assert str(header_obj) == str(header)
def test_v3bwheader_from_text():
""""""
header_obj = V3BwHeader(timestamp, generator_started=generator_started,
earliest_bandwidth=earliest_bandwidth)
header, _ = V3BwHeader().from_text_v110(header_extra_str)
assert str(header_obj) == str(header)
def test_v3bwfile():
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment