blob: b8719a547e264661bf4d1cdac627c9027ebb6e71 [file] [log] [blame]
Ved-vampir0c7e2d42015-03-18 17:18:47 +03001#!/usr/bin/env python
2""" UDP sender class """
3
4import socket
5import urlparse
6
7from cp_protocol import Packet
8from logger import define_logger
9
10
11class SenderException(Exception):
12 """ Exceptions in Sender class """
13 pass
14
15
16class Timeout(Exception):
17 """ Exceptions in Sender class """
18 pass
19
20
21class Sender(object):
22 """ UDP sender class """
23
24 def __init__(self, url=None, port=None, host="127.0.0.1", size=256):
25 """ Create connection object from input udp string or params"""
26
27 # test input
28 if url is None and port is None:
29 raise SenderException("Bad initialization")
30 if url is not None:
31 data = urlparse.urlparse(url)
32 # check schema
33 if data.scheme != "udp":
34 mes = "Bad protocol type: %s instead of UDP" % data.scheme
35 logger.error(mes)
36 raise SenderException("Bad protocol type")
37 # try to get port
38 try:
39 int_port = int(data.port)
40 except ValueError:
41 logger.error("Bad UDP port")
42 raise SenderException("Bad UDP port")
43 # save paths
44 self.sendto = (data.hostname, int_port)
45 self.bindto = (data.hostname, int_port)
46 # try to get size
47 try:
48 self.size = int(data.path.strip("/"))
49 except ValueError:
50 logger.error("Bad packet part size")
51 raise SenderException("Bad packet part size")
52 else:
53 # url is None - use size and port
54 self.sendto = (host, port)
55 self.bindto = ("0.0.0.0", port)
56 self.size = size
57
58 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
59 self.binded = False
60 self.all_data = {}
61 self.send_packer = None
62
63
64 def bind(self):
65 """ Prepare for listening """
66 self.sock.bind(self.bindto)
67 self.sock.settimeout(0.5)
68 self.binded = True
69
70
71 def send(self, data):
72 """ Send data to udp socket"""
73 if self.sock.sendto(data, self.sendto) != len(data):
74 mes = "Cannot send data to %s:%s" % self.sendto
75 logger.error(mes)
76 raise SenderException("Cannot send data")
77
78
79 def send_by_protocol(self, data):
80 """ Send data by Packet protocol
81 data = dict"""
82 if self.send_packer is None:
83 self.send_packer = Packet()
84 parts = self.send_packer.create_packet_v2(data, self.size)
85 for part in parts:
86 self.send(part)
87
88
89 def recv(self):
90 """ Receive data from udp socket"""
91 # check for binding
92 if not self.binded:
93 self.bind()
94 # try to recv
95 try:
96 data, (remote_ip, _) = self.sock.recvfrom(self.size)
97 return data, remote_ip
98 except socket.timeout:
99 raise Timeout()
100
101
102 def recv_by_protocol(self):
103 """ Receive data from udp socket by Packet protocol"""
104 data, remote_ip = self.recv()
105
106 if remote_ip not in self.all_data:
107 self.all_data[remote_ip] = Packet()
108
109 return self.all_data[remote_ip].new_packet(data)
110
111
112 def recv_with_answer(self, stop_event=None):
113 """ Receive data from udp socket and send 'ok' back
114 Command port = local port + 1
115 Answer port = local port
116 Waiting for command is blocking """
117 # create command socket
118 command_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
119 command_port = self.bindto[1]+1
120 command_sock.bind(("0.0.0.0", command_port))
121 command_sock.settimeout(1)
122 # try to recv
123 while True:
124 try:
125 data, (remote_ip, _) = command_sock.recvfrom(self.size)
126 self.send("ok")
127 return data, remote_ip
128 except socket.timeout:
129 if stop_event is not None and stop_event.is_set():
130 # return None if we are interrupted
131 return None
132
133
134 def verified_send(self, send_host, message, max_repeat=20):
135 """ Send and verify it by answer not more then max_repeat
136 Send port = local port + 1
137 Answer port = local port
138 Return True if send is verified """
139 # create send socket
140 send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
141 send_port = self.sendto[1]+1
142 for repeat in range(0, max_repeat):
143 send_sock.sendto(message, (send_host, send_port))
144 try:
145 data, remote_ip = self.recv()
146 if remote_ip == send_host and data == "ok":
147 return True
148 else:
149 logger.warning("No answer from %s, try %i", send_host, repeat)
150 except Timeout:
151 logger.warning("No answer from %s, try %i", send_host, repeat)
152
153 return False
154
155
156
157logger = define_logger(__name__)