blob: 962bf0a8ee500f32d9c564d799941365b2a65745 [file] [log] [blame]
Ved-vampir0c7e2d42015-03-18 17:18:47 +03001#!/usr/bin/env python
2""" Protocol class """
3
4import re
5import zlib
6import json
7import binascii
Ved-vampir0c7e2d42015-03-18 17:18:47 +03008
koder aka kdanilove06762a2015-03-22 23:32:09 +02009try:
10 from disk_perf_test_tool.logger import define_logger
11 logger = define_logger(__name__)
12except ImportError:
13 class Logger(object):
14 def debug(self, *dt):
15 pass
16 logger = Logger()
Ved-vampir0c7e2d42015-03-18 17:18:47 +030017
18# protocol contains 2 type of packet:
19# 1 - header, which contains template schema of counters
20# 2 - body, which contains only values in order as in template
Ved-vampir2c2f2e92015-03-18 18:02:25 +030021# it uses msgpack (or provided packer) for optimization
Ved-vampir0c7e2d42015-03-18 17:18:47 +030022#
23# packet has format:
24# begin_data_prefixSIZE\n\nDATAend_data_postfix
25# packet part has format:
26# SIZE\n\rDATA
27#
28# DATA use archivation
29
30
31class PacketException(Exception):
32 """ Exceptions from Packet"""
33 pass
34
35
36class Packet(object):
37 """ Class proceed packet by protocol"""
38
39 prefix = "begin_data_prefix"
40 postfix = "end_data_postfix"
41 header_prefix = "template"
42 # other fields
43 # is_begin
44 # is_end
45 # crc
46 # data
47 # data_len
48
Ved-vampir2c2f2e92015-03-18 18:02:25 +030049 def __init__(self, packer):
Ved-vampir0c7e2d42015-03-18 17:18:47 +030050 # preinit
51 self.is_begin = False
52 self.is_end = False
53 self.crc = None
54 self.data = ""
55 self.data_len = None
56 self.srv_template = None
57 self.clt_template = None
58 self.tmpl_size = 0
Ved-vampir2c2f2e92015-03-18 18:02:25 +030059 self.packer = packer
Ved-vampir0c7e2d42015-03-18 17:18:47 +030060
Ved-vampir0c7e2d42015-03-18 17:18:47 +030061 def new_packet(self, part):
62 """ New packet adding """
63 # proceed packet
64 try:
65 # get size
66 local_size_s, _, part = part.partition("\n\r")
67 local_size = int(local_size_s)
68
69 # find prefix
70 begin = part.find(self.prefix)
71 if begin != -1:
72 # divide data if something before begin and prefix
73 from_i = begin + len(self.prefix)
74 part = part[from_i:]
75 # reset flags
76 self.is_begin = True
77 self.is_end = False
78 self.data = ""
79 # get size
80 data_len_s, _, part = part.partition("\n\r")
81 self.data_len = int(data_len_s)
82 # get crc
83 crc_s, _, part = part.partition("\n\r")
84 self.crc = int(crc_s)
85
86 # bad size?
87 if local_size != self.data_len:
88 raise PacketException("Part size error")
89
90 # find postfix
91 end = part.find(self.postfix)
92 if end != -1:
93 # divide postfix
94 part = part[:end]
95 self.is_end = True
96
97 self.data += part
98 # check if it is end
99 if self.is_end:
100 self.data = zlib.decompress(self.data)
101 if self.data_len != len(self.data):
102 raise PacketException("Total size error")
103 if binascii.crc32(self.data) != self.crc:
104 raise PacketException("CRC error")
105
106 # check, if it is template
107 if self.data.startswith(self.header_prefix):
108 self.srv_template = self.data
109 # template is for internal use
110 return None
111
112 # decode values list
Ved-vampir2c2f2e92015-03-18 18:02:25 +0300113 vals = self.packer.unpack(self.data)
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300114 dump = self.srv_template % tuple(vals)
115 return dump
116 else:
117 return None
118
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300119 except PacketException as e:
120 # if something wrong - skip packet
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300121 logger.warning("Packet skipped: %s", e)
122 self.is_begin = False
123 self.is_end = False
124 return None
125
126 except TypeError:
127 # if something wrong - skip packet
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300128 logger.warning("Packet skipped: doesn't match schema")
129 self.is_begin = False
130 self.is_end = False
131 return None
132
133 except:
134 # if something at all wrong - skip packet
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300135 logger.warning("Packet skipped: something is wrong")
136 self.is_begin = False
137 self.is_end = False
138 return None
139
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300140 @staticmethod
141 def create_packet(data, part_size):
142 """ Create packet divided by parts with part_size from data
143 No compression here """
144 # prepare data
145 data_len = "%i\n\r" % len(data)
146 header = "%s%s%s\n\r" % (Packet.prefix, data_len, binascii.crc32(data))
147 compact_data = zlib.compress(data)
148 packet = "%s%s%s" % (header, compact_data, Packet.postfix)
149
150 partheader_len = len(data_len)
151
152 beg = 0
153 end = part_size - partheader_len
154
155 result = []
156 while beg < len(packet):
157 block = packet[beg:beg+end]
158 result.append(data_len + block)
159 beg += end
160
161 return result
162
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300163 def create_packet_v2(self, data, part_size):
164 """ Create packet divided by parts with part_size from data
165 Compressed """
166 result = []
167 # create and add to result template header
168 if self.srv_template is None:
169 perf_string = json.dumps(data)
170 self.create_answer_template(perf_string)
171 template = self.header_prefix + self.srv_template
172 header = Packet.create_packet(template, part_size)
173 result.extend(header)
174
175 vals = self.get_matching_value_list(data)
Ved-vampir2c2f2e92015-03-18 18:02:25 +0300176 body = self.packer.pack(vals)
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300177 parts = Packet.create_packet(body, part_size)
178 result.extend(parts)
179 return result
180
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300181 def get_matching_value_list(self, data):
182 """ Get values in order server expect"""
183 vals = range(0, self.tmpl_size)
184
185 try:
186 for node, groups in self.clt_template.items():
187 for group, counters in groups.items():
188 for counter, index in counters.items():
koder aka kdanilove06762a2015-03-22 23:32:09 +0200189 if not isinstance(index, dict):
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300190 vals[index] = data[node][group][counter]
191 else:
192 for k, i in index.items():
193 vals[i] = data[node][group][counter][k]
194
195 return vals
196
197 except (IndexError, KeyError):
198 logger = logging.getLogger(__name__)
199 logger.error("Data don't match last schema")
200 raise PacketException("Data don't match last schema")
201
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300202 def create_answer_template(self, perf_string):
203 """ Create template for server to insert counter values
204 Return tuple of server and clien templates + number of replaces"""
205 replacer = re.compile(": [0-9]+\.?[0-9]*")
206 # replace all values by %s
207 finditer = replacer.finditer(perf_string)
208 # server not need know positions
209 self.srv_template = ""
210 # client need positions
211 clt_template = ""
212 beg = 0
213 k = 0
214 # this could be done better?
215 for match in finditer:
216 # define input place in server template
217 self.srv_template += perf_string[beg:match.start()]
218 self.srv_template += ": %s"
219 # define match number in client template
220 clt_template += perf_string[beg:match.start()]
221 clt_template += ": %i" % k
222
223 beg = match.end()
224 k += 1
225
226 # add tail
227 self.srv_template += perf_string[beg:]
228 clt_template += perf_string[beg:]
229
230 self.tmpl_size = k
231 self.clt_template = json.loads(clt_template)