jumpserver运行源码
本文运行流程介绍来自jumpserver版本号:v2.23.2
入口文件 run_server.py
run_server中通过subprocess.call,用python3运行了同级目录下jms,并传入参数 start all,进入jms.py
首先配置BASE_DIR和APP_DIR全局变量,设置DJANGO_SETTINGS_MODULE值为jumpserver.settings后调用django.steup(),这里就不说了,djang.steup()源码流程可看这里,然后配置logging格式以及必要导入成功检测,并初始化logging对象和创建static和media文件夹,准备工作第一步完成。源码如下:
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
APP_DIR = os.path.join(BASE_DIR, 'apps') os.chdir(APP_DIR)
sys.path.insert(0, APP_DIR)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jumpserver.settings")
django.setup() logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") try:
from jumpserver import const
__version__ = const.VERSION
except ImportError as e:
print("Not found __version__: {}".format(e))
print("Python is: ")
logging.info(sys.executable)
__version__ = 'Unknown'
sys.exit(1) try:
from jumpserver.const import CONFIG
from common.utils.file import download_file
except ImportError as e:
print("Import error: {}".format(e))
print("Could not find config file, `cp config_example.yml config.yml`")
sys.exit(1) os.environ["PYTHONIOENCODING"] = "UTF-8" logging.basicConfig(
format='%(asctime)s %(message)s', level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S'
) logger = logging.getLogger() try:
os.makedirs(os.path.join(BASE_DIR, "data", "static"))
os.makedirs(os.path.join(BASE_DIR, "data", "media"))
except:
pass
然后到文件最后,创建parse,并准备参数配置,格式化参数生产参数对象args(注:这里的args为全局变量,所以jms中的函数可以直接使用该变量,不用当参数传递),args.action为"start",因此走start_services,该部分源码如下:
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="""
Jumpserver service control tools; Example: \r\n %(prog)s start all -d;
"""
)
parser.add_argument(
'action', type=str,
choices=("start", "stop", "restart", "status", "upgrade_db", "collect_static"),
help="Action to run"
)
parser.add_argument(
"services", type=str, default='all', nargs="*",
choices=("all", "web", "task"),
help="The service to start",
)
parser.add_argument('-d', '--daemon', nargs="?", const=True)
parser.add_argument('-w', '--worker', type=int, nargs="?", default=4)
parser.add_argument('-f', '--force', nargs="?", const=True) args = parser.parse_args() action = args.action
if action == "upgrade_db":
upgrade_db()
elif action == "collect_static":
collect_static()
else:
start_services()
start_services源码如下:
def start_services():
services = args.services if isinstance(args.services, list) else [args.services]
if action == 'start' and {'all', 'web'} & set(services):
prepare() start_args = []
if args.daemon:
start_args.append('--daemon')
if args.worker:
start_args.extend(['--worker', str(args.worker)])
if args.force:
start_args.append('--force') try:
management.call_command(action, *services, *start_args)
except KeyboardInterrupt:
logging.info('Cancel ...')
time.sleep(2)
except Exception as exc:
logging.error("Start service error {}: {}".format(services, exc))
time.sleep(2)
services为"all",因此会执行prepare()函数,该函数中分别执行:1.调用django接口检查数据库连接是否正常 2.收集静态文件和数据库迁移 3.清除缓存 4.下载两个ip库,源码较多也简单,就不贴了。最后通过django接口management.call_command调用start并传入all、--daemon、--worker 4、--force参数,这里讲下management.call_command,源码精简贴下,不重要的就不贴了,如下:
def call_command(command_name, *args, **options):
"""
Call the given command, with the given options and args/kwargs. This is the primary API you should use for calling specific commands. `command_name` may be a string or a command object. Using a string is
preferred unless the command object is required for further processing or
testing. Some examples:
call_command('migrate')
call_command('shell', plain=True)
call_command('sqlmigrate', 'myapp') from django.core.management.commands import flush
cmd = flush.Command()
call_command(cmd, verbosity=0, interactive=False)
# Do something with cmd ...
"""
if isinstance(command_name, BaseCommand):
# Command object passed in.
command = command_name
command_name = command.__class__.__module__.split('.')[-1]
else:
# Load the command object by name.
try:
app_name = get_commands()[command_name]
except KeyError:
raise CommandError("Unknown command: %r" % command_name) if isinstance(app_name, BaseCommand):
# If the command is already loaded, use it directly.
command = app_name
else:
command = load_command_class(app_name, command_name) # 中间省略 。。。 return command.execute(*args, **defaults)
首先command_name为"start"字符串,走else,再看app_name = get_commands()[command_name],get_commands()在django.steup()同篇文章django runserver源码中有讲,其实质做的是以字典方式保存django/core/management/commands和所有子app目录/management/commands目录下所有以非"_"开头模块文件名称和app字符串的对应关系,在项目目录下apps/common/management/commands下有start.py文件,因此此时app_name为“common",最后走load_command_class(app_name, command_name),load_command_class源码如下:
def load_command_class(app_name, name):
"""
Given a command name and an application name, return the Command
class instance. Allow all errors raised by the import process
(ImportError, AttributeError) to propagate.
"""
module = import_module('%s.management.commands.%s' % (app_name, name))
return module.Command()
回到call_command函数,此时command为start.py中的Command类(一下简称Command类)的实力对象,最后调用对象的execute()方法,Command类继承BaseActionCommand类,BaseActionCommand类未定义execute方法,于是到BaseCommand类的execute方法,不重要的也不贴了,源码如下:
def execute(self, *args, **options):
"""
Try to execute this command, performing system checks if needed (as
controlled by the ``requires_system_checks`` attribute, except if
force-skipped).
"""
# 前面省略。。。
output = self.handle(*args, **options)
if output:
if self.output_transaction:
connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
output = '%s\n%s\n%s' % (
self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()),
output,
self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()),
)
self.stdout.write(output)
return output
最终调用self.handle()方法,Command类下未定义该方法,于是到BaseActionCommand类,该类handle方法源码如下:
def handle(self, *args, **options):
self.initial_util(*args, **options)
assert self.action in Action.values, f'The action {self.action} is not in the optional list'
_handle = getattr(self, f'_handle_{self.action}', lambda: None)
_handle()
initial_util方法中创建ServicesUtil类的实例属性,并赋值为self.util。创建ServicesUtil类时传入参数分别为:run_daemon:True,stop_daemon:False,force_stop:True。services流程讲一下,源码先贴一下:
@classmethod
def get_service_objects(cls, service_names, **kwargs):
services = set()
for name in service_names:
method_name = f'{name}_services'
if hasattr(cls, method_name):
_services = getattr(cls, method_name)()
elif hasattr(cls, name):
_services = [getattr(cls, name)]
else:
continue
services.update(set(_services)) service_objects = []
for s in services:
service_class = cls.get_service_object_class(s.value)
if not service_class:
continue
kwargs.update({
'name': s.value
})
service_object = service_class(**kwargs)
service_objects.append(service_object)
return service_objects
传入的service_names为["all“],因此_services为[cls.gunicorn, cls.daphne, cls.flower, cls.celery_ansible, cls.celery_default, cls.beat],函数内的services为{cls.gunicorn, cls.daphne, cls.flower, cls.celery_ansible, cls.celery_default, cls.beat},service_objects(返回的services)为services.GunicornService、services.DaphneService、services.FlowerService、services.CeleryDefaultService、services.CeleryAnsibleService、services.BeatService这六个类的实例对象组成的列表。回到handle方法,_handle为self._handle_start(),最后调用该方法,该方法内调用self.util.start_and_watch()方法执行,于是到ServicesUtil.start_and_watch方法,源码如下:
def start_and_watch(self):
logging.info(time.ctime())
logging.info(f'JumpServer version {__version__}, more see https://www.jumpserver.org')
self.start()
if self.run_daemon:
self.show_status()
with self.daemon_context:
self.watch()
else:
self.watch()
先看self.start(),遍历self._services,也就是遍历上述6个类的实例对象,并调用其start方法,这6个类都继承自BaseService类(jumpserver团队自己定义的服务接口类),且6各类均未定义start方法,于是来到BaseService类的start方法,源码如下:
def start(self):
if self.is_running:
self.show_status()
return
self.remove_pid()
self.open_subprocess()
self.write_pid()
self.start_other()
self.is_running是通过对os.kill(self.pid, 0)是否异常来判断该进程号是否在运行,最开始肯定是未运行状态,于是走self.remove_pid(),该方法是通过os.unlink删除tmp文件夹下对应服务的pid文件,然后走self.open_subprocess(),通过subprocess.Popen调用self.cmd,并传入self.cwd和输出文件配置,这里就是真正的开启每个服务了,具体如何开启可以看六个类的cmd属性(property)。然后通过self.write_pid写pid文件,self.start_other是留的一个空函数。回到ServicesUtil.start_and_watch方法,首先self.run_daemon为True,因此调用self.show_status()打印六个服务每个服务是running还是stoped,最后在守护进程上下文内执行self.watch(),self.watch中每隔30秒执行一次self._watch(),self._watch()中是执行六个服务类实例对象的watch方法,服务类对象的watch方法中检查服务是否在运行,如果是未运行状态则重新运行,如果self.watch中捕获到KeyboardInterrupt(键盘退出)异常,则调用self.clean_up()进行清理操作,退出程序。贴下ServicesUtil类的watch()方法,如下:
# -- watch --
def watch(self):
while not self.EXIT_EVENT.is_set():
try:
_exit = self._watch()
if _exit:
break
time.sleep(self.check_interval)
except KeyboardInterrupt:
print('Start stop services')
break
self.clean_up()
jumpserver运行源码的更多相关文章
- MapReduce的ReduceTask任务的运行源码级分析
MapReduce的MapTask任务的运行源码级分析 这篇文章好不容易恢复了...谢天谢地...这篇文章讲了MapTask的执行流程.咱们这一节讲解ReduceTask的执行流程.ReduceTas ...
- 基于案例贯通 Spark Streaming 流计算框架的运行源码
本期内容 : Spark Streaming+Spark SQL案例展示 基于案例贯穿Spark Streaming的运行源码 一. 案例代码阐述 : 在线动态计算电商中不同类别中最热门的商品排名,例 ...
- C#代码反编译 得到项目可运行源码
C#代码反编译 得到项目可运行源码 摘自:http://www.cnblogs.com/know/archive/2011/03/15/1985026.html 谈到"C#代码反编译&quo ...
- Dream_Spark-----Spark 定制版:005~贯通Spark Streaming流计算框架的运行源码
Spark 定制版:005~贯通Spark Streaming流计算框架的运行源码 本讲内容: a. 在线动态计算分类最热门商品案例回顾与演示 b. 基于案例贯通Spark Streaming的运 ...
- C#以管理员权限运行源码,C#软件获取管理员权限,c#获取管理员权限
C#以管理员权限运行源码,C#软件获取管理员权限,c#获取管理员权限 发布时间:2014-10-19 21:40内容来源:未知 点击: 次 windows 7和vista提高的系统的安全性,同时需要明 ...
- 贯通Spark Streaming流计算框架的运行源码
本章节内容: 一.在线动态计算分类最热门商品案例回顾 二.基于案例贯通Spark Streaming的运行源码 先看代码(源码场景:用户.用户的商品.商品的点击量排名,按商品.其点击量排名前三): p ...
- MapReduce的MapTask任务的运行源码级分析
TaskTracker任务初始化及启动task源码级分析 这篇文章中分析了任务的启动,每个task都会使用一个进程占用一个JVM来执行,org.apache.hadoop.mapred.Child方法 ...
- Profession ASP.NET MVC 2.0 NerdDinner示例可运行源码
最近一段时间在看JonGalloway等著作的<Profession ASP.NET MVC 2.0>.本书并没有按照常规的大部头书籍那样,按部就班的介绍MVC的概念等,而是在第一章直接引 ...
- caffe web demo运行+源码分析
caffe web demo学习 1.运行 安装好caffe后,进入/opt/caffe/examples/web_demo/的caffe web demo项目目录,查看一下app.py文件,这是一个 ...
- 从源码开始运行Bitcoin Core
安装Ubuntu 环境:虚拟机 网络连接:桥接 系统版本:16.04 源:ali 安装编译环境(依赖库) sudo apt-get update sudo apt-get install build- ...
随机推荐
- 电脑微信小程序抓包
电脑微信小程序抓包 在渗透的过程中,对于网站找不出什么漏洞的时候我们就可以,对目标进行小程序和公众号漏洞的发掘 0x01 设置微信代理使用Burp抓取数据包 发现我们抓取的数据包很多都是乱码 Prox ...
- 结构型模式 - 代理模式Proxy
学习而来,代码是自己敲的.也有些自己的理解在里边,有问题希望大家指出. 代理模式的定义与特点 代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问.这时,访问对象不 ...
- Solon v2.0 大版本发布。提效率!降成本!
一个高效的 Java 应用开发框架:更快.更小.更简单.不是 Spring,没有 Servlet,也无关 JavaEE:新兴独立的开放生态.主框架仅 0.1 MB. 150来个生态插件,覆盖各种不同的 ...
- 基于View接口
目录 基于View接口 1.写模型表以及数据库迁移 3.使用CBV写查询所有的视图接口 4.配路由 基于View接口 Django原生即继承View来实现写接口,的确过程很繁杂,很多东西都是手动写 ...
- drf-序列化器、反序列化、反序列化校验
1.APIView执行流程 1.之前我们是基于django原生的View编写接口,drf提供给咱们的一个类APIView,以后使用drf写视图类,都是继承这个类及其子类,APIView本身就是继承了D ...
- docker03-常用命令
1.docker基础命令 1.1docker启动 指令:service docker start 1.2查看状态 指令:service docker status 1.3重启docker 指令:ser ...
- Idea移除和删除模块
移除:右键模块-remove moduel 删除:在移除操作后 右键模块-delete 然后删除项目pom文件里面的<moduel>
- .NET 中的并发编程
今天我们购买的每台电脑都有一个多核心的 CPU,允许它并行执行多个指令.操作系统通过将进程调度到不同的内核来发挥这个结构的优点.然而,还可以通过异步 I/O 操作和并行处理来帮助我们提高单个应用程序的 ...
- Elemen ui&表单 、CRUD、安装
ElementUI表单 Form表单,每一个表单域是由一个form-item组件构成的,表单域中可以放置各种类型的表单控键,有input.switch.checkbox 表单的绑定form 内容分别是 ...
- 0x05_My-OS显示字符串和任意参数
先看看效果: 要解决两个问题,第一个如何显示字符串,printf?我之前已经说了所有的头文件都要自己写,printf是stdio里的可是我们没有stdio 我们要通过画像素点的方式显示字符串,有点像我 ...