blob: 1b951f2019ff77c7fd1cb4395725c2e8230b1c92 [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
Ved-vampir2c2f2e92015-03-18 18:02:25 +030024 def __init__(self, packer, url=None, port=None, host="0.0.0.0", size=256):
Ved-vampir0c7e2d42015-03-18 17:18:47 +030025 """ 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
Ved-vampir2c2f2e92015-03-18 18:02:25 +030058 self.packer = packer
59
Ved-vampir0c7e2d42015-03-18 17:18:47 +030060 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
61 self.binded = False
62 self.all_data = {}
63 self.send_packer = None
64
65
66 def bind(self):
67 """ Prepare for listening """
68 self.sock.bind(self.bindto)
69 self.sock.settimeout(0.5)
70 self.binded = True
71
72
73 def send(self, data):
74 """ Send data to udp socket"""
75 if self.sock.sendto(data, self.sendto) != len(data):
76 mes = "Cannot send data to %s:%s" % self.sendto
77 logger.error(mes)
78 raise SenderException("Cannot send data")
79
80
81 def send_by_protocol(self, data):
82 """ Send data by Packet protocol
83 data = dict"""
84 if self.send_packer is None:
Ved-vampir2c2f2e92015-03-18 18:02:25 +030085 self.send_packer = Packet(self.packer())
Ved-vampir0c7e2d42015-03-18 17:18:47 +030086 parts = self.send_packer.create_packet_v2(data, self.size)
87 for part in parts:
88 self.send(part)
89
90
91 def recv(self):
92 """ Receive data from udp socket"""
93 # check for binding
94 if not self.binded:
95 self.bind()
96 # try to recv
97 try:
98 data, (remote_ip, _) = self.sock.recvfrom(self.size)
99 return data, remote_ip
100 except socket.timeout:
101 raise Timeout()
102
103
104 def recv_by_protocol(self):
105 """ Receive data from udp socket by Packet protocol"""
106 data, remote_ip = self.recv()
107
108 if remote_ip not in self.all_data:
Ved-vampir2c2f2e92015-03-18 18:02:25 +0300109 self.all_data[remote_ip] = Packet(self.packer())
Ved-vampir0c7e2d42015-03-18 17:18:47 +0300110
111 return self.all_data[remote_ip].new_packet(data)
112
113
114 def recv_with_answer(self, stop_event=None):
115 """ Receive data from udp socket and send 'ok' back
116 Command port = local port + 1
117 Answer port = local port
118 Waiting for command is blocking """
119 # create command socket
120 command_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
121 command_port = self.bindto[1]+1
122 command_sock.bind(("0.0.0.0", command_port))
123 command_sock.settimeout(1)
124 # try to recv
125 while True:
126 try:
127 data, (remote_ip, _) = command_sock.recvfrom(self.size)
128 self.send("ok")
129 return data, remote_ip
130 except socket.timeout:
131 if stop_event is not None and stop_event.is_set():
132 # return None if we are interrupted
133 return None
134
135
136 def verified_send(self, send_host, message, max_repeat=20):
137 """ Send and verify it by answer not more then max_repeat
138 Send port = local port + 1
139 Answer port = local port
140 Return True if send is verified """
141 # create send socket
142 send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
143 send_port = self.sendto[1]+1
144 for repeat in range(0, max_repeat):
145 send_sock.sendto(message, (send_host, send_port))
146 try:
147 data, remote_ip = self.recv()
148 if remote_ip == send_host and data == "ok":
149 return True
150 else:
151 logger.warning("No answer from %s, try %i", send_host, repeat)
152 except Timeout:
153 logger.warning("No answer from %s, try %i", send_host, repeat)
154
155 return False
156
157
158
159logger = define_logger(__name__)