一、简介

openstack的各个模块中,都有相应的客户端模块实现,其作用是为用户访问具体模块提供了接口,并且也作为模块之间相互访问的途径。Cinder也一样,有着自己的cinder-client。

二、argparse简单介绍

argparse是python用于解析命令行参数和选项的标准模块,作为optparse的一个替代被添加到Python2.7。Cinder-client主要就是调用了argparse这个工具包。

使用步骤:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument()

parser.parse_args()

首先导入该模块;然后创建一个解析对象;然后向该对象中添加你要关注的命令行参数和选项,每一个add_argument方法对应一个你要关注的参数或选项;最后调用parse_args()方法进行解析;解析成功之后即可使用。
方法 ArgumentParser(prog=None, usage=None,description=None, epilog=None, parents=[],formatter_class=argparse.HelpFormatter, prefix_chars='-',fromfile_prefix_chars=None, argument_default=None,conflict_handler='error', add_help=True)

这些参数都有默认值,当调用 parser.print_help()或者运行程序时,由于参数不正确(此时python解释器其实也是调用了pring_help()方法)时,会打印这些描述信息,一般只需要传递description参数

方法add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])
其中:
name or flags:命令行参数名或者选项,如上面的address或者-p,--port.其中命令行参数如果没给定,且没有设置defualt,则出错。但是如果是选项的话,则设置为None。,parse_args()运行时,会用'-'来认证可选参数,剩下的即为位置参数
nargs:命令行参数的个数,一般使用通配符表示,其中,'?'表示只用一个,'*'表示0到多个,'+'表示至少一个。nargs='*' 表示参数可设置零个或多个;nargs=' '+' 表示参数可设置一个或多个;nargs='?'表示参数可设置零个或一个

default:默认值。

type:参数的类型,默认是字符串string类型,还有float、int等类型。
help:和ArgumentParser方法中的参数作用相似,出现的场合也一致。
dest:如果提供dest,例如dest="a",那么可以通过args.a访问该参数
action:参数出发的动作
store:保存参数,默认
store_const:保存一个被定义为参数规格一部分的值(常量),而不是一个来自参数解析而来的值。
store_ture/store_false:保存相应的布尔值
append:将值保存在一个列表中。
append_const:将一个定义在参数规格中的值(常量)保存在一个列表中。
count:参数出现的次数

parser.add_argument("-v", "--verbosity", action="count", default=0, help="increase output verbosity")
version:打印程序版本信息
choice:允许的参数值

三、cinderclient代码入口查找

第一种方式:
D:\官网代码\python-cinderclient-stable-pike\setup.cfg
[entry_points]
console_scripts =
cinder = cinderclient.shell:main 第二种方式:
[root@test bin]# pwd
/usr/bin
[root@test bin]# ls |grep cinder
cinder
cinder-all
cinder-api
cinder-backup
cinder-manage
cinder-rootwrap
cinder-rtstool
cinder-scheduler
cinder-volume
cinder-volume-usage-audit
[root@test bin]# cat cinder
#!/usr/bin/python
# PBR Generated from u'console_scripts'
import sys
from cinderclient.shell import main if __name__ == "__main__":
sys.exit(main())
[root@test bin]#

四、cinderclient代码分析

D:\官网代码\python-cinderclient-stable-pike\cinderclient\shell.py
def main():
try:
if sys.version_info >= (3, 0):-----sys.version获取python的版本,默认情况下, 使用系统自带的python版本,python2.6或者python 2.7
"""
>>> print sys.version_info
(2, 6, 6, 'final', 0)
>>>
"""
OpenStackCinderShell().main(sys.argv[1:])---sys.argv[1:],输入的cinder命令行,sys.argv[0]表示程序本身,sys.argv[1:]表示 输入的参数
else:
OpenStackCinderShell().main([encodeutils.safe_decode(item)----走如下分支,步骤一
for item in sys.argv[1:]])
except KeyboardInterrupt:
print("... terminating cinder client", file=sys.stderr)
sys.exit(130)
except Exception as e:
logger.debug(e, exc_info=1)
print("ERROR: %s" % six.text_type(e), file=sys.stderr)
sys.exit(1)

对步骤一进行详解

from cinderclient import api_versions
from cinderclient import client D:\官网代码\python-cinderclient-stable-pike\cinderclient\shell.py
class OpenStackCinderShell(object): def __init__(self):
self.ks_logger = None
self.client_logger = None
def main(self, argv):
# Parse args once to find version and debug settings
解析args参数一次,查找version和debug设置信息
parser = self.get_base_parser()
"""
get_base_parser:获取基本的命令行解析器;调用add_argument方法实现添加具体命令行参数;
构造参数解析类ArgumentParser的实例parser,然后通过实例调用方法parser.add_argument增加一些固有的参数,比如:--debug,--help,
--os_auth_type等参数
"""
(options, args) = parser.parse_known_args(argv)
"""
parse_known_args()方法的作用就是当仅获取到基本设置时,如果运行命令中传入了之后才会获取到的其他配置,不会报错;
而是将多出来的部分保存起来,留到后面使用,解析的参数按属性的方式存储到Namespace对象;
options的值为命名空间namespace的对象
"""
self.setup_debugging(options.debug)----打开debug信息
api_version_input = True
self.options = options do_help = ('help' in argv) or (-----查看是不是需要对命令行进行help查询
'--help' in argv) or ('-h' in argv) or not argv #确定使用API的版本,默认情况下,是版本3
if not options.os_volume_api_version:
api_version = api_versions.get_api_version(
DEFAULT_MAJOR_OS_VOLUME_API_VERSION)
else:
api_version = api_versions.get_api_version(
options.os_volume_api_version) # build available subcommands based on version
#根据api版本号,去查找其对应的版本的扩展版本,其实本质上就是获取
D:\官网代码\python-cinderclient-stable-pike\cinderclient\v2\contrib\list_extensions.py模块中的类
major_version_string = "%s" % api_version.ver_major
self.extensions = client.discover_extensions(major_version_string)
self._run_extension_hooks('__pre_parse_args__') #基于版本api版本,创建对应的子命令解释器,同时根据对应的api_version版本,加载
D:\官网代码\python-cinderclient-stable-pike\cinderclient不同版本的shell.py文件
D:\官网代码\python-cinderclient-stable-pike\cinderclient\v2\shell.py模块
subcommand_parser = self.get_subcommand_parser(api_version,
do_help, args)
self.parser = subcommand_parser if options.help or not argv:---如果命令行后面跟的是help命令,那么就打印该命令的help信息,直接返回
subcommand_parser.print_help()
return 0 argv = self._delimit_metadata_args(argv)
# 命令行参数的解析;
args = subcommand_parser.parse_args(argv)
self._run_extension_hooks('__post_parse_args__', args) # Short-circuit and deal with help right away.
if args.func == self.do_help:
self.do_help(args)
return 0
elif args.func == self.do_bash_completion:
self.do_bash_completion(args)
return 0
#提取命令行参数中的基本的租户等信息存放到一个元祖里,为后面方法的调用做具体参数的准备
(os_username, os_password, os_tenant_name, os_auth_url,
os_region_name, os_tenant_id, endpoint_type,
service_type, service_name, volume_service_name, os_endpoint,
cacert, os_auth_type) = (
args.os_username, args.os_password,
args.os_tenant_name, args.os_auth_url,
args.os_region_name, args.os_tenant_id,
args.os_endpoint_type,
args.service_type, args.service_name,
args.volume_service_name,
args.os_endpoint, args.os_cacert,
args.os_auth_type)
auth_session = None
#对参数的认证权限的一些处理,比如是否提供租户、是否提供密码等
if os_auth_type and os_auth_type != "keystone":
auth_plugin = loading.load_auth_from_argparse_arguments(
self.options)
auth_session = loading.load_session_from_argparse_arguments(
self.options, auth=auth_plugin)
else:
auth_plugin = None if not service_type:
service_type = client.SERVICE_TYPES[major_version_string] # FIXME(usrleon): Here should be restrict for project id same as
# for os_username or os_password but for compatibility it is not. # V3 stuff
project_info_provided = ((self.options.os_tenant_name or
self.options.os_tenant_id) or
(self.options.os_project_name and
(self.options.os_project_domain_name or
self.options.os_project_domain_id)) or
self.options.os_project_id) # NOTE(e0ne): if auth_session exists it means auth plugin created
# session and we don't need to check for password and other
# authentification-related things.
if not utils.isunauthenticated(args.func) and not auth_session:
if not os_password:
# No password, If we've got a tty, try prompting for it
if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty():
# Check for Ctl-D
try:
os_password = getpass.getpass('OS Password: ')
# Initialize options.os_password with password
# input from tty. It is used in _get_keystone_session.
options.os_password = os_password
except EOFError:
pass
# No password because we didn't have a tty or the
# user Ctl-D when prompted.
if not os_password:
raise exc.CommandError("You must provide a password "
"through --os-password, "
"env[OS_PASSWORD] "
"or, prompted response.") if not project_info_provided:
raise exc.CommandError(_(
"You must provide a tenant_name, tenant_id, "
"project_id or project_name (with "
"project_domain_name or project_domain_id) via "
" --os-tenant-name (env[OS_TENANT_NAME]),"
" --os-tenant-id (env[OS_TENANT_ID]),"
" --os-project-id (env[OS_PROJECT_ID])"
" --os-project-name (env[OS_PROJECT_NAME]),"
" --os-project-domain-id "
"(env[OS_PROJECT_DOMAIN_ID])"
" --os-project-domain-name "
"(env[OS_PROJECT_DOMAIN_NAME])"
)) if not os_auth_url:
raise exc.CommandError(
"You must provide an authentication URL "
"through --os-auth-url or env[OS_AUTH_URL].") if not project_info_provided:
raise exc.CommandError(_(
"You must provide a tenant_name, tenant_id, "
"project_id or project_name (with "
"project_domain_name or project_domain_id) via "
" --os-tenant-name (env[OS_TENANT_NAME]),"
" --os-tenant-id (env[OS_TENANT_ID]),"
" --os-project-id (env[OS_PROJECT_ID])"
" --os-project-name (env[OS_PROJECT_NAME]),"
" --os-project-domain-id "
"(env[OS_PROJECT_DOMAIN_ID])"
" --os-project-domain-name "
"(env[OS_PROJECT_DOMAIN_NAME])"
)) if not os_auth_url and not auth_plugin:
raise exc.CommandError(
"You must provide an authentication URL "
"through --os-auth-url or env[OS_AUTH_URL].")
#没有提供认证会话的,那么与keystone建立认证会话
if not auth_session:
auth_session = self._get_keystone_session() insecure = self.options.insecure self.cs = client.Client(---------------步骤二,本质上是一个http请求
api_version, os_username,
os_password, os_tenant_name, os_auth_url,
region_name=os_region_name,
tenant_id=os_tenant_id,
endpoint_type=endpoint_type,
extensions=self.extensions,
service_type=service_type,
service_name=service_name,
volume_service_name=volume_service_name,
bypass_url=os_endpoint,
retries=options.retries,
http_log_debug=args.debug,
insecure=insecure,
cacert=cacert, auth_system=os_auth_type,
auth_plugin=auth_plugin,
session=auth_session,
logger=self.ks_logger if auth_session else self.client_logger) try:
# 如果所要调用的方法没有标志为unauthenticated,则需要进行身份验证操作;
if not utils.isunauthenticated(args.func):
self.cs.authenticate()
except exc.Unauthorized:
raise exc.CommandError("OpenStack credentials are not valid.")
except exc.AuthorizationFailure:
raise exc.CommandError("Unable to authorize user.") endpoint_api_version = None
# Try to get the API version from the endpoint URL. If that fails fall
# back to trying to use what the user specified via
# --os-volume-api-version or with the OS_VOLUME_API_VERSION environment
# variable. Fail safe is to use the default API setting.
try:
endpoint_api_version = \
self.cs.get_volume_api_version_from_endpoint()
except exc.UnsupportedVersion:
endpoint_api_version = options.os_volume_api_version
if api_version_input:
logger.warning("Cannot determine the API version from "
"the endpoint URL. Falling back to the "
"user-specified version: %s",
endpoint_api_version)
else:
logger.warning("Cannot determine the API version from the "
"endpoint URL or user input. Falling back "
"to the default API version: %s",
endpoint_api_version) profile = osprofiler_profiler and options.profile
if profile:
osprofiler_profiler.init(options.profile) try:
args.func(self.cs, args)----实现根据解析的命令行参数调用具体的方法,假如使用的命令行为cinder list,该处args.func = do_list,
说明这里调用的具体方法是do_list;
finally:
if profile:
trace_id = osprofiler_profiler.get().get_base_id()
print("Trace ID: %s" % trace_id)
print("To display trace use next command:\n"
"osprofiler trace show --html %s " % trace_id)

对步骤二详解

D:\官网代码\python-cinderclient-stable-pike\cinderclient\client.py
def Client(version, *args, **kwargs):
"""Initialize client object based on given version."""
api_version, client_class = _get_client_class_and_version(version)----对步骤2.1 详解根据api版本version版本号,获取对应目录下的Client函数
return client_class(api_version=api_version,*args, **kwargs)-------对步骤2.2的详解 对步骤2.1详解
D:\官网代码\python-cinderclient-stable-pike\cinderclient\client.py
def _get_client_class_and_version(version):
if not isinstance(version, api_versions.APIVersion):
version = api_versions.get_api_version(version)
else:
api_versions.check_major_version(version)
if version.is_latest():
raise exceptions.UnsupportedVersion(
_("The version should be explicit, not latest."))
return version, importutils.import_class(
"cinderclient.v%s.client.Client" % version.ver_major) 对步骤2.2 的详解---假如使用的版本为v2版本
D:\官网代码\python-cinderclient-stable-pike\cinderclient\v2\client.py
from cinderclient import client
from cinderclient import api_versions
from cinderclient.v2 import availability_zones
from cinderclient.v2 import cgsnapshots
from cinderclient.v2 import consistencygroups
from cinderclient.v2 import capabilities
from cinderclient.v2 import limits
from cinderclient.v2 import pools
from cinderclient.v2 import qos_specs
from cinderclient.v2 import quota_classes
from cinderclient.v2 import quotas
from cinderclient.v2 import services
from cinderclient.v2 import volumes
from cinderclient.v2 import volume_snapshots
from cinderclient.v2 import volume_types
from cinderclient.v2 import volume_type_access
from cinderclient.v2 import volume_encryption_types
from cinderclient.v2 import volume_backups
from cinderclient.v2 import volume_backups_restore
from cinderclient.v2 import volume_transfers class Client(object): def __init__(self, username=None, api_key=None, project_id=None,
auth_url='', insecure=False, timeout=None, tenant_id=None,
proxy_tenant_id=None, proxy_token=None, region_name=None,
endpoint_type='publicURL', extensions=None,
service_type='volumev2', service_name=None,
volume_service_name=None, bypass_url=None, retries=0,
http_log_debug=False, cacert=None, auth_system='keystone',
auth_plugin=None, session=None, api_version=None,
logger=None, **kwargs):
# FIXME(comstud): Rename the api_key argument above when we
# know it's not being used as keyword argument
password = api_key
self.version = '2.0'
self.limits = limits.LimitsManager(self) # extensions------引入统一目录下,不同资源的管理类
self.volumes = volumes.VolumeManager(self)
self.volume_snapshots = volume_snapshots.SnapshotManager(self)
self.volume_types = volume_types.VolumeTypeManager(self)
self.volume_type_access = \
volume_type_access.VolumeTypeAccessManager(self)
self.volume_encryption_types = \
volume_encryption_types.VolumeEncryptionTypeManager(self)
self.qos_specs = qos_specs.QoSSpecsManager(self)
self.quota_classes = quota_classes.QuotaClassSetManager(self)
self.quotas = quotas.QuotaSetManager(self)
self.backups = volume_backups.VolumeBackupManager(self)
self.restores = volume_backups_restore.VolumeBackupRestoreManager(self)
self.transfers = volume_transfers.VolumeTransferManager(self)
self.services = services.ServiceManager(self)
self.consistencygroups = consistencygroups.\
ConsistencygroupManager(self)
self.cgsnapshots = cgsnapshots.CgsnapshotManager(self)
self.availability_zones = \
availability_zones.AvailabilityZoneManager(self)
self.pools = pools.PoolManager(self)
self.capabilities = capabilities.CapabilitiesManager(self)
self.api_version = api_version or api_versions.APIVersion(self.version) # Add in any extensions...
if extensions:
for extension in extensions:
if extension.manager_class:
setattr(self, extension.name,
extension.manager_class(self)) if not logger:
logger = logging.getLogger(__name__) self.client = client._construct_http_client(----本质上就是调用http模块的,建立session连接,同时拼接url的请求头,请求体
username=username,
password=password,
project_id=project_id,
auth_url=auth_url,
insecure=insecure,
timeout=timeout,
tenant_id=tenant_id,
proxy_tenant_id=tenant_id,
proxy_token=proxy_token,
region_name=region_name,
endpoint_type=endpoint_type,
service_type=service_type,
service_name=service_name,
volume_service_name=volume_service_name,
bypass_url=bypass_url,
retries=retries,
http_log_debug=http_log_debug,
cacert=cacert,
auth_system=auth_system,
auth_plugin=auth_plugin,
session=session,
api_version=self.api_version,
logger=logger,
**kwargs) def authenticate(self):
"""Authenticate against the server. Normally this is called automatically when you first access the API,
but you can call this method to force authentication right now. Returns on success; raises :exc:`exceptions.Unauthorized` if the
credentials are wrong.
"""
self.client.authenticate() def get_volume_api_version_from_endpoint(self):
return self.client.get_volume_api_version_from_endpoint()  

五、添加一个新的命令行,这个命令行的功能为获取卷的连接信息,

1)命令行设计的样式如下所示:

cinder create-target volume_id hostname ip initiator
usage: cinder create [--platform <platform>]
[--do_local_attach <do_local_attach>]
[--os_type <os_type>]
[--multipath <multipath>]
[<volume_id>]
[<host_ip >]
[<host_name>]
[<initiator>]
Creates a volume target.
Positional arguments:
<volume_id> volume uuid
<host_ip > host ip ,which attach volume
<host_name> host name ,which attach volume
<initiator> host iscsi client,which attach volume Optional arguments:
--platform <platform> host architecture ,which attach volume.Default=x86_64
--do_local_attach <do_local_attach> if or not attach volume.Default=None
--os_type <os_type> host operation system,whiic attach volume.Default=linux2
--multipath <multipath> if or not iscsi multipath.Default=None

 

2)代码实现,在v2版本的shell.py文件中新增一个do_create_target函数

D:\官网代码\python-cinderclient-stable-ocata\cinderclient\v2\shell.py
@utils.arg('volume',
metavar='<volume_id>',
help='volume uuid')
@utils.arg('host_ip',
metavar='<host_ip>',
help='host ip ,which attach volume')
@utils.arg('host_name',
metavar='<host_name>',
help='host name ,which attach volume')
@utils.arg('initiator',
metavar='<initiator>',
help='host iscsi client,which attach volume')
@utils.arg('--platform',
metavar='<platform>',
default='x86_64',
help='host architecture ,which attach volume.Default=x86_64')
@utils.arg('--do_local_attach',
metavar='<do_local_attach>',
default='false',
help='if or not attach volume.Default=false')
@utils.arg('--os_type',
metavar='<os_type>',
default='linux2',
help='host operation system,whiic attach volume.Default=linux2')
@utils.arg('--multipath',
metavar='<multipath>',
default='false',
help='if or not iscsi multipath.Default=false')
def do_create_target(cs,args):
"""create volume iscsi target."""
volume_id=args.volume
connector={}
connector['ip']=args.host_ip
connector['host']=args.host_name
connector['initiator']=args.initiator
connector['platform']=args.platform
connector['do_local_attach']=args.do_local_attach
connector['os_type']=args.os_type
connector['multipath']=args.multipath
info=cs.volumes.initialize_connection(volume_id,connector)
utils.print_dict(info)

输出内容如下:

[root@test ~]# cinder help create-target
usage: cinder create-target [--platform <platform>]
[--do_local_attach <do_local_attach>]
[--os_type <os_type>] [--multipath <multipath>]
<volume_id> <host_ip> <host_name> <initiator> create volume iscsi target. Positional arguments:
<volume_id> volume uuid
<host_ip> host ip ,which attach volume
<host_name> host name ,which attach volume
<initiator> host iscsi client,which attach volume Optional arguments:
--platform <platform>
host architecture ,which attach volume.Default=x86_64
--do_local_attach <do_local_attach>
if or not attach volume.Default=false
--os_type <os_type> host operation system,whiic attach
volume.Default=linux2
--multipath <multipath>
if or not iscsi multipath.Default=false
[root@test ~]#

3)验证命令行

[root@test ~]# cinder --debug create-target a0ac29de-3a16-4b07-9aac-de63ccdf8fda 10.27.244.149 my03n010027244149.sncloud.com \
iqn.1994-05.com.redhat:7329936b16d9
DEBUG:keystoneauth:REQ: curl -g -i -X POST http://10.27.241.34:8776/v2/d432ed8741cc427da398e4239f44deb4/volumes/a0ac29de-3a16-4b07-9aac-de63ccdf8fda/action
-H "User-Agent: python-cinderclient"
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "X-Auth-Token: {SHA1}10e60c88cab7620ad3864cb1110d2b9c64a8170f"
-d
{
"os-initialize_connection": {
"connector": {
"initiator": "iqn.1994-05.com.redhat:7329936b16d9",
"ip": "10.27.244.149",
"platform": "x86_64",
"host": "my03n010027244149.sncloud.com",
"do_local_attach": "false",
"os_type": "linux2",
"multipath": "false"
}
}
} RESP BODY:
{
"connection_info": {
"driver_volume_type": "iscsi",
"data": {
"target_luns": [0],
"target_iqns": ["iqn.2010-10.org.openstack:volume-a0ac29de-3a16-4b07-9aac-de63ccdf8fda"],
"auth_password": "24do7AqfnLDZ5DyB",
"target_discovered": false,
"encrypted": false,
"qos_specs": null,
"target_iqn": "iqn.2010-10.org.openstack:volume-a0ac29de-3a16-4b07-9aac-de63ccdf8fda",
"target_portal": "10.27.244.144:3260",
"volume_id": "a0ac29de-3a16-4b07-9aac-de63ccdf8fda",
"target_lun": 0,
"access_mode": "rw",
"auth_username": "2y35wC68BsvU8M37tWCn",
"auth_method": "CHAP",
"target_portals": ["10.27.244.144:3260"]
}
}
}

  

  

 

  

  

 

cinderclient命令行源码解析的更多相关文章

  1. 在远程登陆的主机上通过命令行源码编译安装 GNU M4、autoconf、automake 等程序

    由于实验需要,最近获得了一个实验室服务器的账号,平常主要通过 ssh 进行远程登陆进行实验.一方面,远程登录的机器只提供终端界面,一般只通过命令行进行任务操作:另一方面,由于是多人共享服务器,故而个人 ...

  2. Redis系列(九):数据结构Hash源码解析和HSET、HGET命令

    2.源码解析 1.相关命令如下: {"hset",hsetCommand,,"wmF",,NULL,,,,,}, {"hsetnx",hse ...

  3. Redis系列(十):数据结构Set源码解析和SADD、SINTER、SDIFF、SUNION、SPOP命令

    1.介绍 Hash是以K->V形式存储,而Set则是K存储,空间节省了很多 Redis中Set是String类型的无序集合:集合成员是唯一的. 这就意味着集合中不能出现重复的数据.可根据应用场景 ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  5. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  6. 实战录 | Kafka-0.10 Consumer源码解析

    <实战录>导语 前方高能!请注意本期攻城狮幽默细胞爆表,坐地铁的拉好把手,喝水的就建议暂时先别喝了:)本期分享人为云端卫士大数据工程师韩宝君,将带来Kafka-0.10 Consumer源 ...

  7. QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数

    QT源码解析(一) QT创建窗口程序.消息循环和WinMain函数 分类: QT2009-10-28 13:33 17695人阅读 评论(13) 收藏 举报 qtapplicationwindowse ...

  8. Celery 源码解析五: 远程控制管理

    今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...

  9. junit源码解析--初始化阶段

    OK,我们接着上篇整理.上篇博客中已经列出的junit的几个核心的类,这里我们开始整理junit完整的生命周期. JUnit 的完整生命周期分为 3 个阶段:初始化阶段.运行阶段和结果捕捉阶段. 这篇 ...

随机推荐

  1. Django学习路36_函数参数 反向解析 修改404 页面

    在 templates 中创建对应文件名的 html 文件 (.html) 注: 开发者服务器发生变更是因为 python 代码发生变化 如果 html 文件发生变化,服务器不会进行重启 需要自己手动 ...

  2. Seaborn实现多变量分析

    import seaborn as sns import numpy as np import pandas as pd import matplotlib.pyplot as plt sns.set ...

  3. mongoDB数据库原生配置

    最近小冷在工作中使用到了mongoDB数据库,所以就简单的写了个demo,和大家简单分享下,如果大家也有想分享的东西或者需要分享的东西,生活或者其他都行,可以关注小冷公众号秦川以北或者加小冷微信qxy ...

  4. C#与网络相关的两个监听的事件

    今天遇到一个问题,当网络连接有问题的时候设计软件向用户发送通知,查了资料发现了两个相关的事件分享一下. 一.System.Net.NetworkInformation命名空间下的NetworkChan ...

  5. 【mysql数据库基础】

    基础:·数据库的本质是一个文件·行---记录·列---字段·RDBMS是一个程序·SQL是结构化的查询语言·MYSQL是一个数据库软件,可以通过SQL操作MYSQL数据库·SQL语句不区分大小写·学了 ...

  6. 【BZOJ4318】OSU! 题解(期望)

    题目链接 题目大意:给定$n$个操作的成功率$p[i]$.连续成功操作$m$次可以贡献$m^3$的分数.问期望分数. 对于$(x+1)^3$ $=x^3+3x^2+3x+1$ 每多连续成功一次,对答案 ...

  7. Linux下关闭防火墙

    1:查看防火状态 **systemctl status firewalld** **service iptables status** 2:暂时关闭防火墙 systemctl stop firewal ...

  8. webMvcConfigurer的详情

                  摘要 Spring的WebMvcConfigurer接口提供了很多方法让我们来定制SpringMVC的配置.而且Spring还提供了WebMvcConfigurerAdap ...

  9. Java异常机制,自定义异常以及spring boot异常设计方案

    异常机制: 异常概念 异常分类 异常的处理方法 自定义异常 springboot 的异常解决方案

  10. 栈及其简单应用(二)(python代码)

    一.括号判定 前一篇文章我们介绍了栈的简单应用中,关于括号的判定,但那只是一种括号的判定,下面我们来介绍多种括号混合使用时,如何判断括号左右一一对应. 比如“{}{(}(][”这种情况,需要对一种括号 ...