-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1ddba50
commit f2170d7
Showing
16 changed files
with
562 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Distribution / packaging | ||
.Python | ||
env/ | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
|
||
# PyInstaller | ||
# Usually these files are written by a python script from a template | ||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
.tox/ | ||
.coverage | ||
.coverage.* | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
*,cover | ||
.hypothesis/ | ||
|
||
# Translations | ||
*.mo | ||
*.pot | ||
|
||
# Django stuff: | ||
*.log | ||
local_settings.py | ||
|
||
# Flask stuff: | ||
instance/ | ||
.webassets-cache | ||
|
||
# Scrapy stuff: | ||
.scrapy | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# PyBuilder | ||
target/ | ||
|
||
# Jupyter Notebook | ||
.ipynb_checkpoints | ||
|
||
# pyenv | ||
.python-version | ||
|
||
# celery beat schedule file | ||
celerybeat-schedule | ||
|
||
# dotenv | ||
.env | ||
|
||
# virtualenv | ||
.venv/ | ||
venv/ | ||
ENV/ | ||
|
||
# Spyder project settings | ||
.spyderproject | ||
|
||
# Rope project settings | ||
.ropeproject | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2016 Instana | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
include LICENSE | ||
include README.md | ||
include requirements.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import sys | ||
import instana.sensor as s | ||
import instana.options as o | ||
import logging as l | ||
|
||
def main(argv): | ||
sensor = s.Sensor(o.Options(service='python-simple', | ||
log_level=l.DEBUG)) | ||
|
||
if __name__ == "__main__": | ||
main(sys.argv) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
""" | ||
Instana sensor and tracer. It consists of two modules that can be used as entry points: | ||
- sensor: activates the meter to collect and transmit all kind of built-in metrics | ||
- tracer: OpenTracing tracer implementation. It implicitly activates the meter | ||
""" | ||
|
||
__author__ = 'Instana Inc.' | ||
__copyright__ = 'Copyright 2016 Instana Inc.' | ||
__credits__ = ['Pavlo Baron'] | ||
__license__ = 'MIT' | ||
__version__ = '0.0.1' | ||
__maintainer__ = 'Pavlo Baron' | ||
__email__ = '[email protected]' | ||
|
||
__all__ = ['sensor', 'tracer'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import json | ||
import thread | ||
import urllib2 | ||
import instana.log as l | ||
import instana.fsm as f | ||
|
||
class From(object): | ||
pid = "" | ||
hostId = "" | ||
def __init__(self, **kwds): | ||
self.__dict__.update(kwds) | ||
|
||
class Head(urllib2.Request): | ||
def get_method(self): | ||
return "HEAD" | ||
|
||
class Put(urllib2.Request): | ||
def get_method(self): | ||
return "PUT" | ||
|
||
class Agent(object): | ||
AGENT_DISCOVERY_URL = "/com.instana.plugin.python.discovery" | ||
AGENT_TRACES_URL = "/com.instana.plugin.python/traces." | ||
AGENT_DATA_URL = "/com.instana.plugin.python." | ||
AGENT_DEFAULT_HOST = "localhost" | ||
AGENT_DEFAULT_PORT = 42699 | ||
AGENT_HEADER = "Instana Agent" | ||
|
||
sensor = None | ||
host = AGENT_DEFAULT_HOST | ||
fsm = None | ||
from_ = None | ||
|
||
def __init__(self, sensor): | ||
l.debug("initializing agent") | ||
|
||
self.sensor = sensor | ||
self.fsm = f.Fsm(self) | ||
self.reset() | ||
|
||
def to_json(self, o): | ||
return json.dumps(o, default=lambda o: o.__dict__, sort_keys=False, indent=4) | ||
|
||
def can_send(self): | ||
return self.fsm.fsm.current == "ready" | ||
|
||
def head(self, url): | ||
return self.request(url, "HEAD", None) | ||
|
||
def request(self, url, method, o): | ||
return self.full_request_response(url, method, o, False, "") | ||
|
||
def request_response(self, url, method, o): | ||
return self.full_request_response(url, method, o, True, "") | ||
|
||
def request_header(self, url, method, header): | ||
return self.full_request_response(url, method, None, False, header) | ||
|
||
def full_request_response(self, url, method, o, body, header): | ||
b = None | ||
h = None | ||
try: | ||
if method == "HEAD": | ||
request = Head(url) | ||
elif method == "GET": | ||
request = urllib2.Request(url) | ||
elif method == "PUT": | ||
request = Put(url, self.to_json(o)) | ||
request.add_header("Content-Type", "application/json") | ||
else: | ||
request = urllib2.Request(url, self.to_json(o)) | ||
request.add_header("Content-Type", "application/json") | ||
|
||
response = urllib2.urlopen(request, timeout=2) | ||
|
||
if not response: | ||
self.reset() | ||
else: | ||
if response.getcode() < 200 or response.getcode() >= 300: | ||
l.error("Request returned erroneous code", response.getcode()) | ||
if self.can_send(): | ||
self.reset() | ||
else: | ||
if body: | ||
b = response.read() | ||
|
||
if header: | ||
h = response.info().getheader(header) | ||
|
||
if method == "HEAD": | ||
b = True | ||
except Exception as e: | ||
l.error(str(e)) | ||
|
||
return (b, h) | ||
|
||
def make_url(self, prefix): | ||
return self.make_host_url(self.host, prefix) | ||
|
||
def make_host_url(self, host, prefix): | ||
port = self.sensor.options.agent_port | ||
if port == 0: | ||
port = self.AGENT_DEFAULT_PORT | ||
|
||
return self.make_full_url(host, port, prefix) | ||
|
||
def make_full_url(self, host, port, prefix): | ||
s = "http://%s:%s%s" % (host, str(port), prefix) | ||
if self.from_.pid != 0: | ||
s = "%s%s" % (s, self.from_.pid) | ||
|
||
return s | ||
|
||
def reset(self): | ||
self.from_ = From() | ||
self.fsm.reset() | ||
|
||
def set_host(self, host): | ||
self.host = host | ||
|
||
def set_from(self, from_): | ||
self.from_ = From(**json.loads(from_)) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import subprocess | ||
import os | ||
import sys | ||
import threading as t | ||
import fysom as f | ||
import instana.log as l | ||
|
||
class Discovery(object): | ||
pid = 0 | ||
name = None | ||
args = None | ||
def __init__(self, **kwds): | ||
self.__dict__.update(kwds) | ||
|
||
class Fsm(object): | ||
E_START = "start" | ||
E_LOOKUP = "lookup" | ||
E_ANNOUNCE = "announce" | ||
E_TEST = "test" | ||
|
||
RETRY_PERIOD = 5#30 | ||
|
||
agent = None | ||
fsm = None | ||
|
||
def __init__(self, agent): | ||
l.debug("initializing fsm") | ||
|
||
self.agent = agent | ||
self.fsm = f.Fysom({ | ||
"initial": "none", | ||
"events": [ | ||
{"name": self.E_START, "src": ["none", "unannounced", "announced", "ready"], "dst": "init"}, | ||
{"name": self.E_LOOKUP, "src": "init", "dst": "unannounced"}, | ||
{"name": self.E_ANNOUNCE, "src": "unannounced", "dst": "announced"}, | ||
{"name": self.E_TEST, "src": "announced", "dst": "ready"}], | ||
"callbacks": { | ||
"onstart": self.lookup_agent_host, | ||
"onenterunannounced": self.announce_sensor, | ||
"onenterannounced": self.test_agent}}) | ||
|
||
def reset(self): | ||
self.fsm.start() | ||
|
||
def lookup_agent_host(self, e): | ||
if self.agent.sensor.options.agent_host != "": | ||
host = self.agent.sensor.options.agent_host | ||
else: | ||
host = self.agent.AGENT_DEFAULT_HOST | ||
|
||
h = self.check_host(host) | ||
if h == self.agent.AGENT_HEADER: | ||
self.agent.set_host(host) | ||
self.fsm.lookup() | ||
else: | ||
self.check_host(self.get_default_gateway()) | ||
if h == self.agent.AGENT_HEADER: | ||
self.agent.set_host(host) | ||
self.fsm.lookup() | ||
|
||
def get_default_gateway(self): | ||
l.debug("checking default gateway") | ||
proc = subprocess.Popen("/sbin/ip route | awk '/default/ { print $3 }'", stdout=subprocess.PIPE) | ||
|
||
return proc.stdout.read() | ||
|
||
def check_host(self, host): | ||
l.debug("checking host", host) | ||
|
||
(b, h) = self.agent.request_header(self.agent.make_host_url(host, "/"), "GET", "Server") | ||
|
||
return h | ||
|
||
def announce_sensor(self, e): | ||
l.debug("announcing sensor to the agent") | ||
|
||
d = Discovery(pid=os.getpid(), | ||
name=sys.executable, | ||
args=sys.argv[0:]) | ||
|
||
(b, h) = self.agent.request_response(self.agent.make_url(self.agent.AGENT_DISCOVERY_URL), "PUT", d) | ||
if not b: | ||
l.error("Cannot announce sensor. Scheduling retry.") | ||
self.schedule_retry(self.announce_sensor, e) | ||
else: | ||
self.agent.set_from(b) | ||
self.fsm.announce() | ||
|
||
def schedule_retry(self, f, e): | ||
t.Timer(self.RETRY_PERIOD, f, [e]).start() | ||
|
||
def test_agent(self, e): | ||
l.debug("testing communication with the agent") | ||
|
||
(b, h) = self.agent.head(self.agent.make_url(self.agent.AGENT_DATA_URL)) | ||
|
||
if not b: | ||
self.schedule_retry(self.test_agent, e) | ||
else: | ||
self.fsm.test() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import logging as l | ||
|
||
logger = l.getLogger('instana') | ||
|
||
def init(level): | ||
ch = l.StreamHandler() | ||
f = l.Formatter('%(asctime)s: %(levelname)s: %(name)s: %(message)s') | ||
ch.setFormatter(f) | ||
logger.addHandler(ch) | ||
logger.setLevel(level) | ||
|
||
def debug(s, *args): | ||
logger.debug("%s %s" % (s, ' '.join(args))) | ||
|
||
def info(s, *args): | ||
logger.info("%s %s" % (s, ' '.join(args))) | ||
|
||
def warn(s, *args): | ||
logger.warn("%s %s" % (s, ' '.join(args))) | ||
|
||
def error(s, *args): | ||
logger.error("%s %s" % (s, ' '.join(args))) |
Oops, something went wrong.