blob: d3305abc4e78ee5ddf06805a2d94325e3b57766f [file] [log] [blame]
Cheng Tang05eec9b2013-07-09 09:33:37 +08001# -*- coding: utf-8
2"""
3agentlink 模块,提供与 swagent 通讯协议的封装
4"""
5import codecs
6import socket
7import struct
8import random
9import time
10import os
11import traceback
12
13__all__ = ['VERSION', 'SWAgentLink']
14
15VERSION = "4.0.0"
16
17
18class SWAgentLink(object):
19 """
20 agentlink 协议封装
21 """
22 MSG_LEN_FMT = '!H'
23 MSG_HEADER_FMT = '!IIIBB'
24 MSG_HEADER_DESC = ('major_version', 'minor_version', 'unique_no', 'msg_type', 'retcode')
25 # 协议版本号定义
Cheng Tang2d6d0852013-07-09 17:06:53 +080026 MAJOR_VERSION = 2
Cheng Tang05eec9b2013-07-09 09:33:37 +080027 MINOR_VERSION = 1
28
29 def __init__(self):
30 self.sock = None
31 self.ip = None
32 self.port = None
33 self.timeout = 10.0
34 self._is_login = False
35 self.unique_no = None
36 self.connection_id = None
37 random.seed(time.time())
38
39 def connect(self, ip, port):
40 self.close()
41 try:
42 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
43 sock.settimeout(self.timeout)
44 sock.connect((ip, port))
45 self.sock = sock
46 self.ip = ip
47 self.port = port
48 return True
49 except socket.error:
50 return False
51
52 def login(self):
53 msg_type = 0x30
54 process_id = struct.pack('!I', os.getpid())
55 message_header = self._new_message(msg_type)
56 login_data = self._pack_request(message_header, process_id)
57 data = self._send_and_recv(login_data, 5.0)
58 if not data:
59 return False
60 login_resp = self._unpack_request(data)
61 if not self._check_response(message_header, login_resp):
62 print u"Login response error"
63 return False
64 if login_resp['retcode'] != 0:
65 print u"Login error retcode={0}".format(login_resp['retcode'])
66 return False
67 self.connection_id = login_resp['data']
68 self._is_login = True
69 return True
70
71 def _send_and_recv(self, request, timeout):
72 header_len = struct.calcsize(self.MSG_LEN_FMT)
73 try:
74 self.sock.settimeout(timeout)
75 self.sock.send(request)
76 # print u"Send[{0}]".format(codecs.encode(request, 'hex'))
77 header = self.sock.recv(header_len)
78 if not header:
79 return None
80 body_len = struct.unpack(self.MSG_LEN_FMT, header)[0]
81 body = self.sock.recv(body_len)
82 # print u"Recv[{0}]".format(codecs.encode(body, 'hex'))
83 return body
84 except socket.error as e:
85 print u"socket send and recv error, error: <{0}>".format(e)
86 traceback.print_exc()
87 self.sock.close()
88 self.sock = None
89 self._is_login = False
90 return None
91
92 def get_data(self):
93 msg_type = 0x32
94 message_header = self._new_message(msg_type)
95 req_data = self._pack_request(message_header, self.connection_id)
96 data = self._send_and_recv(req_data, None)
97 if not data:
98 return False
99 getdata_resp = self._unpack_request(data)
100 if not self._check_response(message_header, getdata_resp):
101 print u"get data error"
102 return None
103 if getdata_resp['retcode'] != 0:
104 print u"GetData error retcode={0}".format(getdata_resp['retcode'])
105 return None
106 data = getdata_resp['data']
107 # print codecs.encode(data, 'hex')
108 data_head_len = struct.calcsize('!I')
109 data_len = struct.unpack('!I', data[:data_head_len])[0]
110 if data_len != len(data) - data_head_len:
111 return None
112 return data[data_head_len: data_head_len + data_len]
113
114 def answer_data(self, response):
115 msg_type = 0x34
116 message_header = self._new_message(msg_type)
117 req_data = self._pack_request(message_header, self.connection_id + response)
118 data = self._send_and_recv(req_data, None)
119 answer_resp = self._unpack_request(data)
120 if not self._check_response(message_header, answer_resp):
121 print u"answer data error"
122 return False
123 if answer_resp['retcode'] != 0:
124 print u"Answer error retcode={0}".format(answer_resp['retcode'])
125 return False
126 return True
127
Cheng Tang2d6d0852013-07-09 17:06:53 +0800128 def answer_invalidate(self):
129 msg_type = 0x36
130 message_header = self._new_message(msg_type)
131 req_data = self._pack_request(message_header, self.connection_id)
132 data = self._send_and_recv(req_data, None)
133 answer_resp = self._unpack_request(data)
134 if not self._check_response(message_header, answer_resp):
135 print u"answer data error"
136 return False
137 if answer_resp['retcode'] != 0:
138 print u"Answer error retcode={0}".format(answer_resp['retcode'])
139 return False
140 return True
141
Cheng Tang05eec9b2013-07-09 09:33:37 +0800142 def _new_message(self, msg_type):
143 unique_no = random.randint(0x10000000, 0xFFFFFFFF)
144 return dict(major_version=self.MAJOR_VERSION, minor_version=self.MINOR_VERSION,
145 unique_no=unique_no, msg_type=msg_type, retcode=0)
146
147 def _pack_request(self, message_header, data):
148 header = struct.pack(self.MSG_HEADER_FMT, message_header['major_version'],
149 message_header['minor_version'], message_header['unique_no'],
150 message_header['msg_type'], message_header['retcode'])
151 r = []
152 if not data:
153 r.append(struct.pack(self.MSG_LEN_FMT, len(header)))
154 r.append(data)
155 else:
156 r.append(struct.pack(self.MSG_LEN_FMT, (len(header) + len(data))))
157 r.append(header)
158 r.append(data)
159 return ''.join(r)
160
161 def close(self):
162 if self.sock:
163 self.sock.close()
164 self.sock = None
165 self._is_login = False
166
167 def is_ok(self):
168 if not self.sock:
169 return False
170 if not self._is_login:
171 return False
172 return True
173
174 def _unpack_request(self, data):
175 if not data:
176 return None
177 header_len = struct.calcsize(self.MSG_HEADER_FMT)
178 if header_len > len(data):
179 return None
180 resp = struct.unpack(self.MSG_HEADER_FMT, data[:header_len])
181 resp_dict = dict(zip(self.MSG_HEADER_DESC, resp))
182 resp_dict['data'] = data[header_len:]
183 return resp_dict
184
185 def _check_response(self, message_header, response):
186 if not response:
187 return False
188 if (message_header['major_version'] != response['major_version']
189 or message_header['minor_version'] != response['minor_version']
190 or message_header['unique_no'] != response['unique_no']
191 or message_header['msg_type'] != response['msg_type']):
192 return False
193 return True