技术标签: python
大家好,小编为大家解答python自动化运维需要掌握的技能的问题。很多人还不知道python自动化运维快速入门 pdf,现在让我们一起来看看吧!
Source code download: 本文相关源码
在介绍 telnetlib 中各个 read 函数的意义前,首先了解 telnetlib 的工作原理。
telnetlib 首先通过 socket 连接从网络接收数据,把数据存储到自己的 raw queue 中,然后对其进行(telnet 协议相关的)处理(cook)python用turtle画三角形。处理结果存放在 cooked queue 中供应用程序取用。整个过程如下图所示:
read_until(expected, timeout=None)
读取直到遇到给定的预期字节字符串,或者直到超时时间。
read_all( )
读取所有数据,直到EOF以字节为单位;阻塞直到连接关闭。
netmiko针对不同网络设备做了优化,可以更有效地处理SSH连接,它还支持不同的设备厂商和平台。
在使用netmiko模块时,不可避免地会使用到ConnectHandler函数,ConnectHandler函数提供定义的字典来开始创建连接。
以下是ConnectHandler函数的定义及示例
定义
def ConnectHandler(*args, **kwargs):
"""Factory function selects the proper class and creates object based on device_type."""
device_type = kwargs["device_type"]
if device_type not in platforms:
if device_type is None:
msg_str = platforms_str
else:
msg_str = telnet_platforms_str if "telnet" in device_type else platforms_str
raise ValueError(
"Unsupported 'device_type' "
"currently supported platforms are: {}".format(msg_str)
)
ConnectionClass = ssh_dispatcher(device_type)
return ConnectionClass(*args, **kwargs)
示例
def netmiko_connect(request):
"""Connect to arista1 and return connection object"""
password = os.getenv("PYNET_PASSWORD") if os.getenv("PYNET_PASSWORD") else getpass()
arista1 = {
"device_type": "arista_eos",
"host": "arista1.lasthop.io",
"username": "pyclass",
"password": password,
}
net_connect = ConnectHandler(**arista1)
def fin():
net_connect.disconnect()
request.addfinalizer(fin)
return net_connect
net_connect.send_config_from_file() # 发送从文件加载的配置命令
net_connect.save_config() # 将running#config保存到startup#config
net_connect.send_command_timing() # 沿通道发送命令,返回输出(基于时序)
net_connect.find_prompt() # 返回当前路由器提示符
net_connect.commit() # 在Juniper和IOS#XR上执行提交操作
net_connect.write_channel() # 通道的低级写入
net_connect.read_channel() # 通道的低级写入
YAML是一种直观的能够被电脑识别的的数据序列化格式,是一个专门用来写配置文件的语言,容易被人类阅读,并且容易和脚本语言交互。
# yaml键值对嵌套:即python中字典嵌套字典
usr1:
name: a
psw: 123
usr2:
name: b
psw: 456
python解析该yaml文件后为
{'usr1': {'name': 'a', 'psw': 123}, 'usr2': {'name': 'b', 'psw': 456}}
# yaml键值对中嵌套数组
usr3:
- a
- b
- c
usr4:
- b
python解析该yaml文件后为
{'usr3': ['a', 'b', 'c'], 'usr4': ['b']}
# 分段yaml文件中多个文档
---
animal1: dog
age: 2
---
animal2: cat
age: 3
import yaml
f = open(r'E:\AutomaticTest\Test_Framework\config\config.yml')
y = yaml.load(f)
print (y)
import yaml
f = '''
---
name: James
age: 20
---
name: Lily
age: 19
'''
y = yaml.load_all(f)
for data in y:
print(data)
输出结果为:
{'name': 'James', 'age': 20}
{'name': 'Lily', 'age': 19}
import yaml
aproject = {'name': 'Silenthand Olleander',
'race': 'Human',
'traits': ['ONE_HAND', 'ONE_EYE']
}
print(yaml.dump(aproject,第二个为可选参数))
输出结果为:
name: Silenthand Olleander
race: Human
traits:
- ONE_HAND
- ONE_EYE
yaml.dump接收的第二个参数一定要是一个打开的文本文件或二进制文件,yaml.dump会把生成的yaml文档写到文件里。
import yaml
obj1 = {"name": "James", "age": 20}
obj2 = ["Lily", 19]
with open(r'E:\AutomaticTest\Test_Framework\config\config.yml', 'w') as f:
yaml.dump_all([obj1, obj2], f)
两段数据参数用列表传入
输出结果为:
{age: 20, name: James}
--- [Lily, 19]
defaults: &defaults
adapter: postgres
host: localhost
development:
database: myapp_development
<<: *defaults
test:
database: myapp_test
<<: *defaults
相当于:
defaults:
adapter: postgres
host: localhost
development:
database: myapp_development
adapter: postgres
host: localhost
test:
database: myapp_test
adapter: postgres
host: localhost
& 用来建立锚点(defaults),<< 表示合并到当前数据,* 用来引用锚点。
这个类的实例用于存储配置、全局对象,并用于从文件系统或其它位置加载模板。模板在Python中广泛使用,模板简单来说就是一个其中包涵占位变量表示动态的部分的文件,模板文件在经过动态赋值后,返回给用户(可以理解为渲染)。
jinja2之所以被广泛使用是因为它具有以下优点:
作为一个模板系统,它还提供了特殊的语法,我们按照它支持的语法进行编写之后,就能使用jinja2模块进行渲染。
在jinja2中,存在三种语法:
{# This is jinja code
filenames = {
{ filenames }}
{% for file in filenames %}
...
{% endfor %}
#}
变量可以通过“过滤器”进行修改,过滤器可以理解为是jinja2里面的内置函数和字符串处理函数。
用法示例:
{
{ 'abc' | captialize }}
# Abc
{
{ 'abc' | upper }}
# ABC
{
{ 'hello world' | title }}
# Hello World
{
{ "hello world" | replace('world','daxin') | upper }}
# HELLO DAXIN
{
{ 18.18 | round | int }}
# 18
大多数应用都在初始化的时候撞见一个Environment对象,并用它加载模板。Environment支持两种加载方式:
以PackageLoader为例:
from jinja2 import PackageLoader,Environment
file_loader = PackageLoader('templates') 我们定义一个PackageLoader。 # 从templates目录中检索模板。
env = Environment(loader=PackageLoader('python_project','templates')) # 创建一个包加载器对象
template = env.get_template('bast.html') # 使用get_template()方法获得模板
template.render(name='daxin',age=18) # 渲染
PackageLoader()的两个参数为:python包的名称,以及模板目录名称。
FileSystemLoader:文件系统加载器,不需要模板文件存在某个Python包下,可以直接访问系统中的文件。
以FileSystemLoader为例的继承示例:
from jinja2 import Environment, FileSystemLoader
content = 'This is about page'
file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)
template = env.get_template('about.html')
output = template.render(content=content)
print(output)
subprocess使用时,父进程创建子进程去执行一个外部程序,并提供了标准输入输出和管道(pipe)的实现方法,同时获取它们的返回码。
subprocess.Popen类用于在一个新进程中执行一个子程序,程序运行subprocess.Popen()类,父进程创建子进程后,不会等待子进程执行完成。如果需要等待子进程,需要加入wait()方法阻塞父进程。
subprocess.Popen(args[, bufsize, stdin, stdout, stderr, …]):Popen类的构造函数,返回结果为subprocess.Popen对象。
如:PopenObject为subprocess.Popen( )对象
PopenObject.communicate([input, timeout]):与进程进行交互(如发送数据到stdin、读取stdout和stderr数据),它会阻塞父进程,直到子进程完成。
input:表示将发送到子进程的字符串数据,默认为None;
timeout:超时判断,若超过timeout秒后仍未结束则抛出TimeoutExpired异常;
communicate返回值:一个元组(stdout_data, stderr_data)
PopenObject.poll() :用于检查命令是否已经执行结束,若结束返回状态码;若未结束返回None
PopenObject.wait([timeout, endtime]):等待子进程结束,并返回状态码;若超过timeout(s)进程仍未结束,则抛出异常
PopenObject.send_signal(signal):发送信号signal给子进程
PopenObject.terminate():停止子进程
PopenObject.kill():杀死子进程
subprocess.call(args[, stdout, …]):执行args命令,返回值为命令执行状态码(类似os.system);
若未指定stdout,则命令执行后的结果输出到屏幕;
若指定stdout,则命令执行后的结果输出到stdout;
若执行成功,则函数返回值为0;若执行失败,则函数返回值为1;
subprocess.check_call(args[, stdout, …]):执行args命令,返回值为命令执行状态码;
若未指定stdout,则命令执行后的结果输出到屏幕;
若指定stdout,则命令执行后的结果输出到stdout;
若执行成功,则函数返回值为0;若执行失败,抛出异常;
subprocess.check_output(args[, stderr, …]):执行args命令,返回值为命令执行的输出结果;
若执行成功,则函数返回值为命令输出结果;若执行失败,则抛出异常;
以subprocess.call()为例,用法如下:
child = subprocess.call('python --version', shell =True)
print(child)
def subprocess_Popen1():
print("***通过communicate函数分别输出PopenObject对象的输出流和错误流***")
args = [["adb", "devices"], ["adb", "devices11"]]
for arg in args:
popen_object = subprocess.Popen(arg, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
object_stdout, object_stderr = popen_object.communicate()
output = {"popen_object": popen_object,
"object_stdout": object_stdout,
"object_stderr": object_stderr}
print(output)
"""
{'popen_object': <subprocess.Popen object at 0x0000000002212400>, 'object_stdout': b'List of devices attached \r\n106D111805005938\tdevice\r\n\r\n', 'object_stderr': b''}
{'popen_object': <subprocess.Popen object at 0x0000000002577C18>, 'object_stdout': b'', 'object_stderr': b'Android Debug Bridge version 1.0.31\r\n\r\n -a .....}
"""
print("***通过stdout和stderr方法输出PopenObject对象输出流和错误流***")
p0 = subprocess.Popen(["adb", "devices"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
object_stdout = p0.stdout.read()
p0.stdout.close()
object_stderr = p0.stderr.read()
p0.stderr.close()
print(object_stdout) # 结果:b'List of devices attached \r\n338b123f0504\tdevice\r\n\r\n'
print(object_stderr) # 结果:b''
print("***Popen对象stdin写入功能:使用stdout和stderr输出")
args = ["python", "python1"]
for arg in args:
p4 = subprocess.Popen([arg], shell=True, stdout=subprocess.PIPE,
stdin=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
p4.stdin.write("print('hello')")
p4.stdin.close()
out = p4.stdout.read()
p4.stdout.close()
err = p4.stderr.read()
p4.stderr.close()
print("out:%s err:%s" % (out, err))
"""
***Popen对象stdin写入功能
out:hello
err:
out: err:'python1' 不是内部或外部命令,也不是可运行的程序或批处理文件。
"""
print("***Popen对象stdin写入功能:使用communicate输出")
p4 = subprocess.Popen(["python"], stdout=subprocess.PIPE,
stdin=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
p4.stdin.write("print('hello')")
output = p4.communicate()
print(output) # 结果:('hello\n', '')
print("***不含encoding参数***")
p1 = subprocess.Popen("adb devices", shell=True, stdout=subprocess.PIPE)
out1 = p1.stdout.readlines()
print(out1) # 结果: [b'List of devices attached \r\n', b'106D111805005938\tdevice\r\n', b'\r\n']
print("***含encoding参数***")
p2 = subprocess.Popen("adb devices", shell=True, stdout=subprocess.PIPE, encoding="utf-8")
out2 = p2.stdout.readlines()
print(out2) # 结果: ['List of devices attached \n', '106D111805005938\tdevice\n', '\n']
print("***Popen对象检查命令是否结束,等待进程结束")
print(p2.poll()) # 结果: None
print(p2.wait()) # 结果: 0
print(p2.poll()) # 结果: 0
print("***Popen对象communicate函数,它会阻塞父进程直至子进程完成")
p3 = subprocess.Popen("adb devices", shell=True, stdout=subprocess.PIPE)
out = p3.communicate()[0]
print(out) # 结果:b'List of devices attached \r\n338b123f0504\tdevice\r\n\r\n'
print(p3.poll()) # 结果:0
subprocess_Popen1()
def subprocess_Popen2():
"""
1. 通过管道功能,实现adb shell ps | findstr top功能
2. 直接为args赋值为一个字符串,实现adb shell ps | findstr top功能
:return:
"""
print("***通过管道方式***")
p1 = subprocess.Popen(["adb", "shell", "ps"], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["findstr", "top"], stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p2.communicate()
print(out, err) # 结果:b'shell 8508 8504 2600 1044 c004e5f8 b6f40938 S top\r\r\n' b''
print("***通过传一个字符串方式***")
p3 = subprocess.Popen("adb shell ps | findstr top", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p3.communicate()
print(out, err) # 结果:b'shell 8508 8504 2600 1044 c004e5f8 b6f40938 S top\r\r\n' b''
subprocess_Popen2()
我们创建一个”fabfile.py”文件,”fabfile.py”文件中每个函数就是一个任务,任务名即函数名,例中是”hello”。”fab”命令就是用来执行”fabfile.py”中定义的任务,它必须显式地指定任务名。你可以使用参数”-l”来列出当前”fabfile.py”文件中定义了哪些任务:
def hello():
print "Hello Fabric!"
在”fabfile.py”的目录下执行命令:
$ fab hello
任务可以带参数,比如我们将hello函数改为:
def hello(name, value):
print "Hello Fabric! %s=%s" % (name,value)
此时执行hello任务时,就要传入参数值:
$ fab hello:name=Year,value=2016
“fabric.api”包里的local()方法可以用来执行本地Shell命令,比如让我们列出本地”/home/ckr”目录下的所有文件及目录:
from fabric.api import local
def hello():
local('ls -l /home/ckr/')
local()方法有一个capture参数用来捕获标准输出,比如:
def hello():
output = local('echo Hello', capture=True)
Fabric默认执行的脚本是fabfile.py,如果要换脚本文件需要使用 -f 指定。比如我们将hello任务放到.py中就要执行:
fab -f .py hello
Fabric真正强大之处不是在执行本地命令,而是可以方便的执行远程机器上的Shell命令。它通过SSH实现,你需要的是在脚本中配置远程机器地址及登录信息:
将登陆密码写到脚本文件里是不安全的,推荐的方法是设置SSH KEY自动登陆。
登陆本地机器生成KEY:
$ ssh-keygen -t rsa -f ~/.ssh/id_rsa_fabric
生成密钥对之后将公钥添加到远程服务器的~/.ssh/authorized_keys文件中,就可以实现自动登陆了。
#-*- coding:utf-8 -*-
from fabric.api import env, roles, run, execute, cd
env.hosts = ['10.216.224.65', '10.216.224.66']
env.user = 'user'
env.key_filename = '~/.ssh/id_rsa_fabric'
def taskA():
with cd('/usr/local/webserver'):
run('pwd')
※注意:authorized_keys文件权限只所有者可写,其他用户均无写权限,否则sshd将认为不安全不允许使用该文件导致还需要输入密码认证。
#-*- coding:utf-8 -*-
from fabric.api import run, env
# env被称为环境字典,用来配置一些运行环境相关的信息
env.hosts = ['192.168.1.100', '192.168.1.101']
env.user = 'user'
env.password = 'passwd'
def taskA():
run('cd /usr/local/webserver/php && ls -l')
run('sudo /usr/local/webserver/nginx/sbin/nginx -t')
fabric.api包里的run()方法可以用来执行远程Shell命令。上面的任务会分别到两台服务器”192.168.1.100”和”192.168.1.101”上执行命令。这里假设两台服务器的用户名都是”user”,密码都是“passwd”。
env.hosts是设置机器列表的,也可以把用户直接写到hosts里:
env.hosts = ['[email protected]', '[email protected]']
如果你的env.hosts里没有配置某个服务器,但是你又想在这个服务器上执行任务,你可以在命令行中通过-H指定远程服务器地址,多个服务器地址用逗号分隔:
fab -H 192.168.1.102,192.168.1.103 taskA
如果对于不同的服务器想执行不同的任务,上面的程序就做不到了,我们需要对服务器定义角色:
#-*- coding:utf-8 -*-
from fabric.api import env, roles, run, execute, cd
env.roledefs = {
'dev': ['[email protected]', '[email protected]'],
'online': ['[email protected]']
}
# host strings必须由username@host:port三部分构成,缺一不可,否则运行时还是会要求输入密码
env.passwords = {
'[email protected]:22': 'passwd1',
'[email protected]:22': 'passwd2',
'[email protected]:22': 'passwd3'
}
@roles('dev')
def taskA():
with cd('/usr/local/webserver'):
run('pwd')
@roles('online')
def taskB():
run('pwd')
def task():
execute(taskA)
execute(taskB)
然后执行task任务:
$ fab task
Fabric会在dev机器上执行taskA任务,然后在online机器上执行taskB任务。@roles装饰器指定了它所装饰的任务会被哪个角色的服务器执行。
如果某一任务上没有指定某个角色,但是你又想让这个角色的服务器也能运行该任务,你可以通过-R来指定角色名,多个角色用逗号分隔:
$ fab -R online taskA
到目前为止,我们介绍了local()和run()函数分别用来执行本地和远程Shell命令。Fabric还提供了其他丰富的功能函数来辅助执行命令,这里我们介绍几个常用的:
功能类似于run()方法,区别是它相当于在Shell命令前加上了sudo,所以拥有超级用户的权限。使用此功能前,你需要将你的用户设为sudoer,而且无需输密码。
from fabric.api import env, sudo
env.hosts = ['[email protected]', '[email protected]']
env.password = '111111'
def hello():
sudo('mkdir /var/www/myapp')
它的工作原理是基于scp命令,使用的方法如下:
from fabric.api import env, get
env.hosts = ['[email protected]',]
env.password = '111111'
def hello():
get('/var/log/myapp.log', 'myapp-0301.log')
上述任务将远程机上”/var/log/myapp.log”文件下载到本地当前目录,并命名为”myapp-0301.log”。
同get一样,put方法也是基于scp命令,使用的方法如下:
from fabric.api import env, put
env.hosts = ['[email protected]', '[email protected]']
env.password = '111111'
def hello():
put('/tmp/myapp-0301.tar.gz', '/var/www/myapp.tar.gz')
上述任务将本地”/tmp/myapp-0301.tar.gz”文件分别上传到两台远程机的”/var/www/“目录下,并命名为”myapp.tar.gz”。如果远程机上的目录需要超级用户权限才能放文件,可以在put()方法里加上use_sudo参数:
put('/tmp/myapp-0301.tar.gz', '/var/www/myapp.tar.gz', use_sudo=True)
该方法类似于Shell中的read命令,它会在终端显示一段文字来提示用户输入,并将用户的输入保存在变量里:
from fabric.api import env, get, prompt
env.hosts = ['[email protected]',]
env.password = '111111'
def hello():
filename = prompt('Please input file name: ')
get('/var/log/myapp.log', '%s.log' % filename)
现在下载后的文件名将由用户的输入来决定。我们还可以对用户输入给出默认值及类型检查:
port = prompt('Please input port number: ', default=8080, validate=int)
执行任务后,终端会显示:
Please input port number: [8080]
如果你直接按回车,则”port”变量即为默认值”8080”;如果你输入字符串,终端会提醒你类型验证失败,让你重新输入,直到正确为止。
看方法名就猜到了,有时候安装好环境后,需要重启服务器,这时就要用到reboot()方法,你可以用wait参数来控制其等待多少秒后重启,没有此参数则代表立即重启:
from fabric.api import env, reboot
env.hosts = ['[email protected]',]
env.password = '111111'
def restart():
reboot(wait=60)
上面的restart任务将在一分钟后重启服务器。
Fabric的上下文管理器是一系列与Python的”with”语句配合使用的方法,它可以在”with”语句块内设置当前工作环境的上下文。让我们介绍几个常用的:
cd()方法在之前的范例中出现过,with cd()语句块可以用来设置远程机的工作目录:
from fabric.api import env, cd, put
env.hosts = ['[email protected]', ]
env.password = '111111'
def hello():
with cd('/var/www/'):
put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz')
上例中的文件会上传到远程机的”/var/www/“目录下。出了with cd()语句块后,工作目录就回到初始的状态,也就是”ckr”用户的根目录。
lcd()就是”local cd”的意思,用法同cd()一样,区别是它设置的是本地的工作目录:
from fabric.api import env, cd, lcd, put
env.hosts = ['[email protected]', ]
env.password = '111111'
def hello():
with cd('/var/www/'):
with lcd('/tmp/'):
put('myapp-0301.tar.gz', 'myapp.tar.gz')
这个例子的执行效果跟上个例子一样。
from fabric.api import env, run, path
env.hosts = ['[email protected]', ]
env.password = '111111'
def hello():
with path('/home/ckr/tmp'):
run('echo $PATH')
run('echo $PATH')
假设我们的PATH环境变量默认是”/sbin:/bin”,在上述with path()语句块内PATH变量将变为”/sbin:/bin:/home/ckr/tmp”。出了with语句块后,PATH又回到原来的值。
Fabric环境变量即是我们例子中一直出现的fabric.api.env,它支持的参数可以从官方文档中查到。
from fabric.api import env, run, settings
env.hosts = ['[email protected]', ]
env.password = '111111'
def hello():
with settings(warn_only=True):
run('echo $USER')
我们将环境参数warn_only暂时设为True,这样遇到错误时任务不会退出。
可以用来临时设置远程和本地机上Shell的环境变量。
from fabric.api import env, run, local, shell_env
env.hosts = ['[email protected]', ]
env.password = '111111'
def hello():
with shell_env(JAVA_HOME='/opt/java'):
run('echo $JAVA_HOME')
local('echo $JAVA_HOME')
from fabric.api import env, run, local, prefix
env.hosts = ['[email protected]', ]
env.password = '111111'
def hello():
with prefix('echo Hi'):
run('pwd')
local('pwd')
在上述with prefix()语句块内,所有的run()或local()方法的执行都会加上echo Hi &&前缀,也就是效果等同于:
run('echo Hi && pwd')
local('echo Hi && pwd')
配合后一节我们会讲到的错误处理,它可以确保在prefix()方法上的命令执行成功后才会执行语句块内的命令。
默认情况下,Fabric在任务遇到错误时就会退出,如果我们希望捕获这个错误而不是退出任务的话,就要开启warn_only参数。在上面介绍settings()上下文管理器时,我们已经看到了临时开启warn_only的方法了,如果要全局开启,有两个办法:
$ fab -w hello
from fabric.api import env
env.warn_only = True
现在遇到错误时,控制台会打出一个警告信息,然后继续执行后续任务。那我们怎么捕获错误并处理呢?像run(), local(), sudo(), get(), put()等SSH功能函数都有返回值。当返回值的succeeded属性为True时,说明执行成功,反之就是失败。你也可以检查返回值的failed属性,为True时就表示执行失败,有错误发生。在开启warn_only后,你可以通过failed属性检查捕获错误,并执行相应的操作。示例如下:
from fabric.api import env, cd, put
env.hosts = ['[email protected]', ]
env.password = '111111'
def hello():
with cd('/var/www/'):
upload = put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz')
if upload.failed:
sudo('rm myapp.tar.gz')
put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz', use_sudo=True)
我们在介绍执行远程命令时曾提到过多台机器的任务默认情况下是串行执行的。Fabric支持并行任务,当服务器的任务之间没有依赖时,并行可以有效的加快执行速度。怎么开启并行执行呢?办法也是两个:
$ fab -P hello
from fabric.api import env
env.parallel = True
以上是对任务并行做一个全局控制。如果只想对某一个任务做并行的话,我们可以在任务函数上加上@parallel装饰器,这样即便全局并行未开启,被@parallel装饰的任务也会并行执行:
from fabric.api import parallel
@parallel
def runs_in_parallel():
pass
def runs_serially():
pass
这样即便并行未开启,runs_in_parallel()任务也会并行执行。
反过来,我们可以在任务函数上加上@serial装饰器:
from fabric.api import serial
def runs_in_parallel():
pass
@serial
def runs_serially():
pass
这样即便并行已经开启,runs_serially()任务也会串行执行。
文章浏览阅读652次。Linux的相关概念和理解Linux的基本指令ls,cd,pwd, touch, mkdir, whoami, tree, adduser, userdel实现_桌面操作系统,手机操作系统,服务器操作系统,嵌入式操作系统
文章浏览阅读1.1k次。hostnamectl set-hostname layuiFilehostname #查看主机名然后先跳到普通用户再exit退到root看其生效或reboot重启生效_centos8stream 关于的名称
文章浏览阅读1.1w次。当一个View调用requestLayout的时候,会给当前的View设置一个FORCE_LAYOUT标记。由此向ViewParent请求布局。这样从这个View开始向上一直requestLayout。最终到达ViewRootImpl。ViewParent 就是当前的传输链。【参见职责链设计模式】 第一步。ViewRootImpl发现请求了布局。那么就会调用measure方_kotlin requestlayout
文章浏览阅读1.7k次。import cv2import numpy as npimg1 = cv2.imread('left.png',1)img1 = cv2.resize(img1, (797,1212))dst1 = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)cv2.imwrite('l.png',dst1)img1 = cv2.imread('left.png',1..._python cv2 resize(img1, ())
文章浏览阅读656次。Socket基础五:基于数据报套接字的网络程序(udp丢包测试)作者:刘磊 2020.4.27 参考书目:《Windows网络编程》刘琰等著一、功能函数1、int udp_server_fun_packetloss(SOCKET s){ int iResult = 0; int count = 0; struct sockaddr_in cliaddr; int addrlen = sizeof(sockaddr_in); char recvline[MAXLINE]; do_数据报套接字 丢包测试
文章浏览阅读1.6k次。发现华为云有一个好用的国内镜像kibana各个版本下载地址:https://mirrors.huaweicloud.com/elasticsearch/kibana各个版本下载地址:https://mirrors.huaweicloud.com/kibana/ik分词器各个版本下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases亲测下载速度非常快..._ik分词器国内镜像
文章浏览阅读575次。定义函数 squareThisNumber(x),内容如下:
文章浏览阅读4k次。网站标题:超级应用 - 免费应用内测托管平台|APP应用分发平台|iOS应用分发|Android应用分发|免费应用内测托管平台源码下载网站关键词:超级应用(www.awwjd.com) 为您提供测试测试应用、应用托管、分发测试、兼容测试等,为客户提供APP托管和免费的下载分发渠道!源码下载网站描述:超级应用,分发平台,内测平台,内测分发,app分发,ios企业签名,ios分发平台,ios免签封装,苹果企业签名,苹果超级签名,免签封装,应用打包,苹果App下载,安卓应用,苹果应用,扫码下载,UDID,iO_awjd1.tv
文章浏览阅读45次。# 01MySQL·引擎特性·InnoDBBufferPool# 02AliSQL·特性介绍·动态加字段# 03PgSQL·特性分析·数据库崩溃恢复(上)# 04MySQL·答疑解惑·MySQL的那些网络超时错误# 05HybridDB·最佳实践·HybridDB数据合并的方法与原理...
文章浏览阅读163次。很多人询问关于约束、时序分析的问题,比如:如何设置setup,hold时间?如何使用全局时钟和第二全局时钟(长线资源)?如何进行分组约束?如何约束某部分组合逻辑?如何通过约束保证异步时钟域之间的数据交换可靠?如何使用I/O逻辑单元内部的寄存器资源?如何进行物理区域约束,完成物理综合和物理实现?为了解决大家的疑难,我们将逐一讨论这些问题。(注:以下主要设计时序约束)A 时序约束的概念和基本策略..._如何约束fpga寄存器到pad的延时
文章浏览阅读512次,点赞19次,收藏12次。WebLOAD是一款针对Web应用程序的企业级负载和性能测试工具,提供性能、完整性和可伸缩性测试等功能,能够同时模拟数千个用户,因此您可以测试重流量负载,并报告应用程序中的弱点、约束和性能瓶颈。它是一个以开发人员为中心(当然,测试人员亦可以使用,因为真的很方便),免费和开源的负载测试工具,旨在使性能测试具有生产力和令人愉悦的体验,可最大程度地减少系统资源的消耗。在线性能监控是指借助监控工具,监控系统性能的实际数据,因为是真实数据,比研发环境中通过工具产生负载得到的测试结果更客观,更有分析价值。_web页面性能测试工具
文章浏览阅读511次。 1:进制转换转换规则:先把数据的每一位上的系数乘以对应基数的次幂(低位从零开始),然后相加即可十进制到其他进制规则:除基取余,直到商为0,最后将余数反转十进制到二进制: 除2取余,直到商为0,最后将余数反转 例: 十进制13对应的二进制数据是1101 二进制到十进制的技巧(8421码)十进制到八进制: 除8取余,直到商为0,最后将余数反转 例: ..._java 32进制递增