8
8
import subprocess
9
9
import sys
10
10
import threading
11
+ from typing import TYPE_CHECKING , Any , Callable , Optional
11
12
12
13
from fysom import Fysom
13
14
14
- from .log import logger
15
- from .util import get_default_gateway
16
- from .version import VERSION
15
+ from instana .log import logger
16
+ from instana .util import get_default_gateway
17
+ from instana .version import VERSION
17
18
19
+ if TYPE_CHECKING :
20
+ from instana .agent .host import HostAgent
18
21
19
- class Discovery (object ):
20
- pid = 0
21
- name = None
22
- args = None
23
- fd = - 1
24
- inode = ""
25
22
26
- def __init__ (self , ** kwds ):
23
+ class Discovery :
24
+ pid : int = 0
25
+ name : Optional [str ] = None
26
+ args : Optional [List [str ]] = None
27
+ fd : int = - 1
28
+ inode : str = ""
29
+
30
+ def __init__ (self , ** kwds : Any ) -> None :
27
31
self .__dict__ .update (kwds )
28
32
29
- def to_dict (self ):
30
- kvs = dict ()
31
- kvs [' pid' ] = self .pid
32
- kvs [' name' ] = self .name
33
- kvs [' args' ] = self .args
34
- kvs ['fd' ] = self .fd
35
- kvs [' inode' ] = self .inode
33
+ def to_dict (self ) -> Dict [ str , Any ] :
34
+ kvs : Dict [ str , Any ] = dict ()
35
+ kvs [" pid" ] = self .pid
36
+ kvs [" name" ] = self .name
37
+ kvs [" args" ] = self .args
38
+ kvs ["fd" ] = self .fd
39
+ kvs [" inode" ] = self .inode
36
40
return kvs
37
41
38
42
39
- class TheMachine ( object ) :
43
+ class TheMachine :
40
44
RETRY_PERIOD = 30
41
45
THREAD_NAME = "Instana Machine"
42
46
43
- agent = None
47
+ agent : Optional [ "HostAgent" ] = None
44
48
fsm = None
45
49
timer = None
46
50
47
51
warnedPeriodic = False
48
52
49
- def __init__ (self , agent ) :
53
+ def __init__ (self , agent : "HostAgent" ) -> None :
50
54
logger .debug ("Initializing host agent state machine" )
51
55
52
56
self .agent = agent
53
- self .fsm = Fysom ({
54
- "events" : [
55
- ("lookup" , "*" , "found" ),
56
- ("announce" , "found" , "announced" ),
57
- ("pending" , "announced" , "wait4init" ),
58
- ("ready" , "wait4init" , "good2go" )],
59
- "callbacks" : {
60
- # Can add the following to debug
61
- # "onchangestate": self.print_state_change,
62
- "onlookup" : self .lookup_agent_host ,
63
- "onannounce" : self .announce_sensor ,
64
- "onpending" : self .on_ready ,
65
- "ongood2go" : self .on_good2go }})
57
+ self .fsm = Fysom (
58
+ {
59
+ "events" : [
60
+ ("lookup" , "*" , "found" ),
61
+ ("announce" , "found" , "announced" ),
62
+ ("pending" , "announced" , "wait4init" ),
63
+ ("ready" , "wait4init" , "good2go" ),
64
+ ],
65
+ "callbacks" : {
66
+ # Can add the following to debug
67
+ # "onchangestate": self.print_state_change,
68
+ "onlookup" : self .lookup_agent_host ,
69
+ "onannounce" : self .announce_sensor ,
70
+ "onpending" : self .on_ready ,
71
+ "ongood2go" : self .on_good2go ,
72
+ },
73
+ }
74
+ )
66
75
67
76
self .timer = threading .Timer (1 , self .fsm .lookup )
68
77
self .timer .daemon = True
69
78
self .timer .name = self .THREAD_NAME
70
79
self .timer .start ()
71
80
72
81
@staticmethod
73
- def print_state_change (e ):
74
- logger .debug ('========= (%i#%s) FSM event: %s, src: %s, dst: %s ==========' ,
75
- os .getpid (), threading .current_thread ().name , e .event , e .src , e .dst )
82
+ def print_state_change (e : Any ) -> None :
83
+ logger .debug (
84
+ f"========= ({ os .getpid ()} #{ threading .current_thread ().name } ) FSM event: { e .event } , src: { e .src } , dst: { e .dst } =========="
85
+ )
76
86
77
- def reset (self ):
87
+ def reset (self ) -> None :
78
88
"""
79
89
reset is called to start from scratch in a process. It may be called on first boot or
80
90
after a detected fork.
@@ -87,7 +97,7 @@ def reset(self):
87
97
logger .debug ("State machine being reset. Will start a new announce cycle." )
88
98
self .fsm .lookup ()
89
99
90
- def lookup_agent_host (self , e ) :
100
+ def lookup_agent_host (self , e : Any ) -> bool :
91
101
host = self .agent .options .agent_host
92
102
port = self .agent .options .agent_port
93
103
@@ -105,39 +115,43 @@ def lookup_agent_host(self, e):
105
115
return True
106
116
107
117
if self .warnedPeriodic is False :
108
- logger .info ("Instana Host Agent couldn't be found. Will retry periodically..." )
118
+ logger .info (
119
+ "Instana Host Agent couldn't be found. Will retry periodically..."
120
+ )
109
121
self .warnedPeriodic = True
110
122
111
- self .schedule_retry (self .lookup_agent_host , e , self .THREAD_NAME + ": agent_lookup" )
123
+ self .schedule_retry (
124
+ self .lookup_agent_host , e , f"{ self .THREAD_NAME } : agent_lookup"
125
+ )
112
126
return False
113
127
114
- def announce_sensor (self , e ):
115
- logger .debug ("Attempting to make an announcement to the agent on %s:%d" ,
116
- self .agent .options .agent_host , self .agent .options .agent_port )
128
+ def announce_sensor (self , e : Any ) -> bool :
129
+ logger .debug (
130
+ f"Attempting to make an announcement to the agent on { self .agent .options .agent_host } :{ self .agent .options .agent_port } "
131
+ )
117
132
pid = os .getpid ()
118
133
119
134
try :
120
135
if os .path .isfile ("/proc/self/cmdline" ):
121
136
with open ("/proc/self/cmdline" ) as cmd :
122
137
cmdinfo = cmd .read ()
123
- cmdline = cmdinfo .split (' \x00 ' )
138
+ cmdline = cmdinfo .split (" \x00 " )
124
139
else :
125
140
# Python doesn't provide a reliable method to determine what
126
141
# the OS process command line may be. Here we are forced to
127
142
# rely on ps rather than adding a dependency on something like
128
143
# psutil which requires dev packages, gcc etc...
129
- proc = subprocess .Popen (["ps" , "-p" , str (pid ), "-o" , "command" ],
130
- stdout = subprocess .PIPE )
144
+ proc = subprocess .Popen (
145
+ ["ps" , "-p" , str (pid ), "-o" , "command" ], stdout = subprocess .PIPE
146
+ )
131
147
(out , _ ) = proc .communicate ()
132
- parts = out .split (b' \n ' )
148
+ parts = out .split (b" \n " )
133
149
cmdline = [parts [1 ].decode ("utf-8" )]
134
150
except Exception :
135
151
cmdline = sys .argv
136
152
logger .debug ("announce_sensor" , exc_info = True )
137
153
138
- d = Discovery (pid = self .__get_real_pid (),
139
- name = cmdline [0 ],
140
- args = cmdline [1 :])
154
+ d = Discovery (pid = self .__get_real_pid (), name = cmdline [0 ], args = cmdline [1 :])
141
155
142
156
# If we're on a system with a procfs
143
157
if os .path .exists ("/proc/" ):
@@ -146,47 +160,56 @@ def announce_sensor(self, e):
146
160
# PermissionError: [Errno 13] Permission denied: '/proc/6/fd/8'
147
161
# Use a try/except as a safety
148
162
sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
149
- sock .connect ((self .agent .options .agent_host , self .agent .options .agent_port ))
150
- path = "/proc/%d/fd/%d" % (pid , sock .fileno ())
163
+ sock .connect (
164
+ (self .agent .options .agent_host , self .agent .options .agent_port )
165
+ )
166
+ path = f"/proc/{ pid } /fd/{ sock .fileno ()} "
151
167
d .fd = sock .fileno ()
152
168
d .inode = os .readlink (path )
153
- except :
169
+ except : # noqa: E722
154
170
logger .debug ("Error generating file descriptor: " , exc_info = True )
155
171
156
172
payload = self .agent .announce (d )
157
173
158
174
if not payload :
159
175
logger .debug ("Cannot announce sensor. Scheduling retry." )
160
- self .schedule_retry (self .announce_sensor , e , self .THREAD_NAME + ": announce" )
176
+ self .schedule_retry (
177
+ self .announce_sensor , e , f"{ self .THREAD_NAME } : announce"
178
+ )
161
179
return False
162
-
180
+
163
181
self .agent .set_from (payload )
164
182
self .fsm .pending ()
165
- logger .debug ("Announced pid: %s (true pid: %s). Waiting for Agent Ready..." ,
166
- str (pid ), str (self .agent .announce_data .pid ))
183
+ logger .debug (
184
+ f"Announced PID: { pid } (true PID: { self .agent .announce_data .pid } ). Waiting for Agent Ready..."
185
+ )
167
186
return True
168
187
169
- def schedule_retry (self , fun , e , name ) :
188
+ def schedule_retry (self , fun : Callable , e : Any , name : str ) -> None :
170
189
self .timer = threading .Timer (self .RETRY_PERIOD , fun , [e ])
171
190
self .timer .daemon = True
172
191
self .timer .name = name
173
192
self .timer .start ()
174
193
175
- def on_ready (self , _ ) :
194
+ def on_ready (self , _ : Any ) -> None :
176
195
self .agent .start ()
177
196
178
197
ns_pid = str (os .getpid ())
179
198
true_pid = str (self .agent .announce_data .pid )
180
199
181
- logger .info ("Instana host agent available. We're in business. Announced PID: %s (true pid: %s)" , ns_pid , true_pid )
200
+ logger .info (
201
+ f"Instana host agent available. We're in business. Announced PID: { ns_pid } (true PID: { true_pid } )"
202
+ )
182
203
183
- def on_good2go (self , _ ) :
204
+ def on_good2go (self , _ : Any ) -> None :
184
205
ns_pid = str (os .getpid ())
185
206
true_pid = str (self .agent .announce_data .pid )
186
207
187
- self .agent .log_message_to_host_agent ("Instana Python Package %s: PID %s (true pid: %s) is now online and reporting" % (VERSION , ns_pid , true_pid ))
208
+ self .agent .log_message_to_host_agent (
209
+ f"Instana Python Package { VERSION } : PID { ns_pid } (true PID: { true_pid } ) is now online and reporting"
210
+ )
188
211
189
- def __get_real_pid (self ):
212
+ def __get_real_pid (self ) -> int :
190
213
"""
191
214
Attempts to determine the true process ID by querying the
192
215
/proc/<pid>/sched file. This works on systems with a proc filesystem.
@@ -195,14 +218,14 @@ def __get_real_pid(self):
195
218
pid = None
196
219
197
220
if os .path .exists ("/proc/" ):
198
- sched_file = "/proc/%d/sched" % os .getpid ()
221
+ sched_file = f "/proc/{ os .getpid ()} /sched"
199
222
200
223
if os .path .isfile (sched_file ):
201
224
try :
202
225
file = open (sched_file )
203
226
line = file .readline ()
204
- g = re .search (r' \((\d+),' , line )
205
- if len (g .groups ()) == 1 :
227
+ g = re .search (r" \((\d+)," , line )
228
+ if g and len (g .groups ()) == 1 :
206
229
pid = int (g .groups ()[0 ])
207
230
except Exception :
208
231
logger .debug ("parsing sched file failed" , exc_info = True )
@@ -211,3 +234,6 @@ def __get_real_pid(self):
211
234
pid = os .getpid ()
212
235
213
236
return pid
237
+
238
+
239
+ # Made with Bob
0 commit comments