用paramiko写堡垒机
paramiko
paramiko模块,基于SSH用于连接远程服务器并执行相关操作。
基本用法
SSHClient
基于用户名密码连接:
基础用法:
import paramiko
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='c1.salt.com', port=22, username='wupeiqi', password='123')
# 执行命令
stdin, stdout, stderr = ssh.exec_command('ls')
# 获取命令结果
result = stdout.read()
# 关闭连接
ssh.close()
SSHClient 封装 Transport
import paramiko
transport = paramiko.Transport(('hostname', 22))
transport.connect(username='wupeiqi', password='123')
ssh = paramiko.SSHClient()
ssh._transport = transport
stdin, stdout, stderr = ssh.exec_command('df')
print stdout.read()
transport.close()
由此我们可以看出来,ssh执行命令时,我们可以使用sshclient transport两种方式来实现
基于公钥密钥连接:
基础用法:
import paramiko
private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='c1.salt.com', port=22, username='wupeiqi', key=private_key)
# 执行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 获取命令结果
result = stdout.read()
# 关闭连接
ssh.close()
封装transport:
import paramiko
private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')
transport = paramiko.Transport(('hostname', 22))
transport.connect(username='wupeiqi', pkey=private_key)
ssh = paramiko.SSHClient()
ssh._transport = transport
stdin, stdout, stderr = ssh.exec_command('df')
transport.close()
SFTPClient
用于连接远程服务器并执行上传下载
基于用户名密码上传下载:
import paramiko
transport = paramiko.Transport(('hostname',22))
transport.connect(username='wupeiqi',password='123')
sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
# 将remove_path 下载到本地 local_path
sftp.get('remove_path', 'local_path')
transport.close()
基于公钥密钥上传下载:
import paramiko
private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')
transport = paramiko.Transport(('hostname', 22))
transport.connect(username='wupeiqi', pkey=private_key )
sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
# 将remove_path 下载到本地 local_path
sftp.get('remove_path', 'local_path')
transport.close()
有此看出,如果只做上传下载方式的话,我们只能使用transport,其实无论是ssh,还是sftp,都是调用了transport,基于socket实现的
生产需求:上传某文件并覆盖
demo:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import paramiko
import uuid
class SSHConnection(object):
    def __init__(self, host='172.16.103.191', port=22, username='wupeiqi',pwd='123'):
        self.host = host
        self.port = port
        self.username = username
        self.pwd = pwd
        self.__k = None
    def create_file(self):
        file_name = str(uuid.uuid4())
        with open(file_name,'w') as f:
            f.write('sb')
        return file_name
    def run(self):
        self.connect()
        self.upload('/home/wupeiqi/tttttttttttt.py')
        self.rename('/home/wupeiqi/tttttttttttt.py', '/home/wupeiqi/ooooooooo.py)
        self.close()
    def connect(self):
        transport = paramiko.Transport((self.host,self.port))
        transport.connect(username=self.username,password=self.pwd)
        self.__transport = transport
    def close(self):
        self.__transport.close()
    def upload(self,target_path):
        # 连接,上传
        file_name = self.create_file()
        sftp = paramiko.SFTPClient.from_transport(self.__transport)
        # 将location.py 上传至服务器 /tmp/test.py
        sftp.put(file_name, target_path)
    def rename(self, old_path, new_path):
        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 执行命令
        cmd = "mv %s %s" % (old_path, new_path,)
        stdin, stdout, stderr = ssh.exec_command(cmd)
        # 获取命令结果
        result = stdout.read()
    def cmd(self, command):
        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 执行命令
        stdin, stdout, stderr = ssh.exec_command(command)
        # 获取命令结果
        result = stdout.read()
        return result
ha = SSHConnection()
ha.run()
对于更多限制命令,需要在系统中设置:
位置:/etc/sudoers,代码:
Defaults    requiretty
Defaults:cmdb    !requiretty
堡垒机
执行流程:
- 管理员为用户在服务器上创建账号(将公钥放置服务器,或者使用用户名密码)
- 用户登陆堡垒机,输入堡垒机用户名密码,现实当前用户管理的服务器列表
- 用户选择服务器,并自动登陆
- 执行操作并同时将用户操作记录
需要注意的是,如果想实现用户登录后直接操作,需要配置下堡垒机用户家目录的.bashrc文件:
/usr/bin/env python $PATH/s7.py
logout
将这两行假如即可.
看下实现方式:
简单调用:
import paramiko
import sys
import os
import socket
import select
import getpass
tran = paramiko.Transport(('192.168.4.193', 22,))
tran.start_client()
tran.auth_password('root', '7ujm8ik,')
# 打开一个通道
chan = tran.open_session()
# 获取一个终端
chan.get_pty()
# 激活器
chan.invoke_shell()
#########
# 利用sys.stdin,肆意妄为执行操作
# 用户在终端输入内容,并将内容发送至远程服务器
# 远程服务器执行命令,并将结果返回
# 用户终端显示内容
#########
交互操作(无tab)
import paramiko
import sys
import os
import socket
import select
import getpass
from paramiko.py3compat import u
tran = paramiko.Transport(('10.211.55.4', 22,))
tran.start_client()
tran.auth_password('wupeiqi', '123')
# 打开一个通道
chan = tran.open_session()
# 获取一个终端
chan.get_pty()
# 激活器
chan.invoke_shell()
while True:
    # 监视用户输入和服务器返回数据
    # sys.stdin 处理用户输入
    # chan 是之前创建的通道,用于接收服务器返回信息
    readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1)
    if chan in readable:
        try:
            x = u(chan.recv(1024))
            if len(x) == 0:
                print('\r\n*** EOF\r\n')
                break
            sys.stdout.write(x)
            sys.stdout.flush()
        except socket.timeout:
            pass
    if sys.stdin in readable:
        inp = sys.stdin.readline()
        chan.sendall(inp)
chan.close()
tran.close()
但此次我们会发现,没有tab补全,跟我们真是在shell里执行命令还是略有差距的,那么来第二个.
交互操作(有tab功能)
#!/usr/bin/env python
# -*-coding=utf-8-*-
# Auther:ccorz Mail:ccniubi@163.com Blog:http://www.cnblogs.com/ccorz/
# GitHub:https://github.com/ccorzorz
import paramiko
import sys
import os
import socket
import select
import getpass
import termios
import tty
from paramiko.py3compat import u
tran = paramiko.Transport(('192.168.4.193', 22,))
tran.start_client()
tran.auth_password('root', '7ujm8ik,')
# 打开一个通道
chan = tran.open_session()
# 获取一个终端
chan.get_pty()
# 激活器
chan.invoke_shell()
# 获取原tty属性
oldtty = termios.tcgetattr(sys.stdin)
try:
    # 为tty设置新属性
    # 默认当前tty设备属性:
    #   输入一行回车,执行
    #   CTRL+C 进程退出,遇到特殊字符,特殊处理。
    # 这是为原始模式,不认识所有特殊符号
    # 放置特殊字符应用在当前终端,如此设置,将所有的用户输入均发送到远程服务器
    tty.setraw(sys.stdin.fileno())
    chan.settimeout(0.0)
    while True:
        # 监视 用户输入 和 远程服务器返回数据(socket)
        # 阻塞,直到句柄可读
        r, w, e = select.select([chan, sys.stdin], [], [], 1)
        if chan in r:
            try:
                x = u(chan.recv(1024))
                if len(x) == 0:
                    print('\r\n*** EOF\r\n')
                    break
                sys.stdout.write(x)
                sys.stdout.flush()
            except socket.timeout:
                pass
        if sys.stdin in r:
            x = sys.stdin.read(1)
            if len(x) == 0:
                break
            chan.send(x)
finally:
    # 重新设置终端属性,必须设置,否则再次登录后无法使用
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
chan.close()
tran.close()
生产需求
在生产中,我们还需要更多的需求:
- 有日志记录,我们可以把管理员的每一条记录都记录到数据库中
- tab补全时,记录的中间会有空格之类的需要处理
我们来看下怎么实现:
import paramiko
import sys
import os
import socket
import getpass
# from paramiko.py3compat import u
# windows does not have termios...
try:
    import termios
    import tty
    has_termios = True
except ImportError:
    has_termios = False
def interactive_shell(chan):
    if has_termios:
        posix_shell(chan)
    else:
        windows_shell(chan)
def posix_shell(chan):
    import select
    oldtty = termios.tcgetattr(sys.stdin)
    try:
        tty.setraw(sys.stdin.fileno())
        tty.setcbreak(sys.stdin.fileno())
        chan.settimeout(0.0)
        f = open('handle.log','a+')
        tab_flag = False
        temp_list = []
        while True:
            r, w, e = select.select([chan, sys.stdin], [], [])
            if chan in r:
                try:
                    x = chan.recv(1024)
                    if len(x) == 0:
                        sys.stdout.write('\r\n*** EOF\r\n')
                        break
                    if tab_flag:
                        if x.startswith('\r\n'):
                            pass
                        else:
                            f.write(x)
                            f.flush()
                        tab_flag = False
                    sys.stdout.write(x)
                    sys.stdout.flush()
                except socket.timeout:
                    pass
            if sys.stdin in r:
                x = sys.stdin.read(1)
                if len(x) == 0:
                    break
                if x == '\t':
                    tab_flag = True
                else:
                    f.write(x)
                    f.flush()
                chan.send(x)
    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
def windows_shell(chan):
    import threading
    sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")
    def writeall(sock):
        while True:
            data = sock.recv(256)
            if not data:
                sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
                sys.stdout.flush()
                break
            sys.stdout.write(data)
            sys.stdout.flush()
    writer = threading.Thread(target=writeall, args=(chan,))
    writer.start()
    try:
        while True:
            d = sys.stdin.read(1)
            if not d:
                break
            chan.send(d)
    except EOFError:
        # user hit ^Z or F6
        pass
def run():
    # 获取当前登录用户
"""
    host_list = [
        {'host': "192.168.11.139", 'username': 'oldboy', 'pwd': "123"},
        {'host': "192.168.11.138", 'username': 'oldboy', 'pwd': "123"},
        {'host': "192.168.11.137", 'username': 'oldboy', 'pwd': "123"},
    ]
    for item in enumerate(host_list, 1):
        print(item['host'])
    num = raw_input('序号:')
    sel_host = host_list[int(num) -1]
    hostname = sel_host['host']
    username = sel_host['username']
    pwd = sel_host['pwd']
    print(hostname,username,pwd)
"""
    tran = paramiko.Transport((hostname, 22,))
    tran.start_client()
    tran.auth_password(username, pwd)
    # 打开一个通道
    chan = tran.open_session()
    # 获取一个终端
    chan.get_pty()
    # 激活器
    chan.invoke_shell()
    interactive_shell(chan)
    chan.close()
    tran.close()
if __name__ == '__main__':
    run()
银角究极版
珍藏下吧:
import paramiko
import sys
import os
import socket
import getpass
from paramiko.py3compat import u
# windows does not have termios...
try:
    import termios
    import tty
    has_termios = True
except ImportError:
    has_termios = False
def interactive_shell(chan):
    if has_termios:
        posix_shell(chan)
    else:
        windows_shell(chan)
def posix_shell(chan):
    import select
    oldtty = termios.tcgetattr(sys.stdin)
    try:
        tty.setraw(sys.stdin.fileno())
        tty.setcbreak(sys.stdin.fileno())
        chan.settimeout(0.0)
        log = open('handle.log', 'a+', encoding='utf-8')
        flag = False
        temp_list = []
        while True:
            r, w, e = select.select([chan, sys.stdin], [], [])
            if chan in r:
                try:
                    x = u(chan.recv(1024))
                    if len(x) == 0:
                        sys.stdout.write('\r\n*** EOF\r\n')
                        break
                    if flag:
                        if x.startswith('\r\n'):
                            pass
                        else:
                            temp_list.append(x)
                        flag = False
                    sys.stdout.write(x)
                    sys.stdout.flush()
                except socket.timeout:
                    pass
            if sys.stdin in r:
                x = sys.stdin.read(1)
                import json
                if len(x) == 0:
                    break
                if x == '\t':
                    flag = True
                else:
                    temp_list.append(x)
                if x == '\r':
                    log.write(''.join(temp_list))
                    log.flush()
                    temp_list.clear()
                chan.send(x)
    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
def windows_shell(chan):
    import threading
    sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")
    def writeall(sock):
        while True:
            data = sock.recv(256)
            if not data:
                sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
                sys.stdout.flush()
                break
            sys.stdout.write(data)
            sys.stdout.flush()
    writer = threading.Thread(target=writeall, args=(chan,))
    writer.start()
    try:
        while True:
            d = sys.stdin.read(1)
            if not d:
                break
            chan.send(d)
    except EOFError:
        # user hit ^Z or F6
        pass
def run():
    default_username = getpass.getuser()
    username = input('Username [%s]: ' % default_username)
    if len(username) == 0:
        username = default_username
    hostname = input('Hostname: ')
    if len(hostname) == 0:
        print('*** Hostname required.')
        sys.exit(1)
    tran = paramiko.Transport((hostname, 22,))
    tran.start_client()
    default_auth = "p"
    auth = input('Auth by (p)assword or (r)sa key[%s] ' % default_auth)
    if len(auth) == 0:
        auth = default_auth
    if auth == 'r':
        default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
        path = input('RSA key [%s]: ' % default_path)
        if len(path) == 0:
            path = default_path
        try:
            key = paramiko.RSAKey.from_private_key_file(path)
        except paramiko.PasswordRequiredException:
            password = getpass.getpass('RSA key password: ')
            key = paramiko.RSAKey.from_private_key_file(path, password)
        tran.auth_publickey(username, key)
    else:
        pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
        tran.auth_password(username, pw)
    # 打开一个通道
    chan = tran.open_session()
    # 获取一个终端
    chan.get_pty()
    # 激活器
    chan.invoke_shell()
    interactive_shell(chan)
    chan.close()
    tran.close()
if __name__ == '__main__':
    run()
数据库模型
其实堡垒机的难点是在数据库的设计上
#!/usr/bin/env python
# -*-coding=utf-8-*-
# Auther:ccorz Mail:ccniubi@163.com Blog:http://www.cnblogs.com/ccorz/
# GitHub:https://github.com/ccorzorz
from sqlalchemy import create_engine, and_, or_, func, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, DateTime
from  sqlalchemy.orm import sessionmaker, relationship
engine = create_engine("mysql+pymysql://root:7ujm8ik,@192.168.4.193:3306/test13", max_overflow=5)
Base = declarative_base()  # 生成一个SqlORM 基类
class UserProfile2HostUser(Base):
    __tablename__ = 'userprofile_2_hostuser'
    id = Column(Integer, primary_key=True, autoincrement=True)
    userprofile_id = Column(Integer, ForeignKey('user_profile.id'),primary_key=True)
    hostuser_id = Column(Integer, ForeignKey('host_user.id'),primary_key=True)
    # userprofile = relationship('UserProfile',secondary=lambda :)
class Host(Base):
    __tablename__ = 'host'
    id = Column(Integer, primary_key=True, autoincrement=True)
    hostname = Column(String(64), unique=True, nullable=False)
    ip_addr = Column(String(128), unique=True, nullable=False)
    port = Column(Integer, default=22)
class HostUser(Base):
    __tablename__ = 'host_user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    AuthTypes = [
        (u'ssh-passwd', u'SSH/Password'),
        (u'ssh-key', u'SSH/KEY'),
    ]
    auth_type = Column(String(64))
    username = Column(String(64), nullable=False)
    password = Column(String(255))
    host_id = Column(Integer, ForeignKey('host.id'))
    host = relationship('Host', backref='uu')
    __table_args__ = (UniqueConstraint(u'host_id', u'username', name='_host_username_uc'),)
class Group(Base):
    __tablename__ = 'group'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), unique=True, nullable=False)
#
# obj = session.query(HostUser.username,HostUser.password,Host.hostname,Host.port).join(Host).filter(HostUser.id == 1).first()
# (用户名,密码,主机名,端口)
class UserProfile(Base):
    __tablename__ = 'user_profile'
    id = Column(Integer, primary_key=True)
    username = Column(String(64), unique=True, nullable=False)
    # 存密码感觉没什么卵用
    # password = Column(String(255),nullable=False)
    # 如果是一个人只能在一个组下
    group_id = Column(Integer, ForeignKey('group.id'))
    #需要这么加secondary,否则插入数据时会报错
    host_list = relationship('HostUser', secondary=lambda :UserProfile2HostUser.__table__, backref='userprofiles')
"""
# 输入用户名和密码:
#
obj = session.query(UserProfile).filter(username=输入的用户名, password=输入的密码).first()
if not obj:
	# 堡垒机登录用户对象
	# 输入这个人的所有机器
	obj.host_list # 当前堡垒机登录用户,所有的服务器用户名
	#
	for item  in obj.host_list:
		# item,是一个HostUser对象
		item.password,item.username,
		# item.host 对象,host对象
		item.host.hostname,item.host.port
	# item 目标机器HostUser对象
	host_obj = input(:...)
	session.add(AuditLog(userprofile_id=obj.id,hostuser_id = host_obj.id, "ifconfig"))
"""
class Log(Base):
    __tablename__ = 'log'
    id = Column(Integer, primary_key=True)
    userprofile_id = Column(Integer, ForeignKey('user_profile.id'))
    hostuser_id = Column(Integer, ForeignKey('host_user.id'))
    cmd = Column(String(255))
    date = Column(DateTime)
# class Session:
#     session = None
#     def __init__(self):
#         self.engine = create_engine("mysql+pymysql://root:7ujm8ik,@192.168.4.193:3306/test13", max_overflow=5)
#         self.ss = sessionmaker(bind=self.engine)
#         self.obj = ss()
#         self.Session.session = obj
#
#     @classmethod
#     def instance(cls):
#         if not cls.session:
#             cls()
#         return cls.session
# ss = Session()
# 定义初始化数据库函数
def init_db():
    Base.metadata.create_all(engine)
# init_db()
# 删除数据库函数
def drop_db():
    Base.metadata.drop_all(engine)
# drop_db()
# 实例化数据库操作对象为session
Session = sessionmaker(bind=engine)
ss = Session()
#
# ss.add_all([
#     Group(id=1, name='DBA'),
#     Group(id=2, name='SA')
# ])
#
# ss.add_all([
#     UserProfile(id=1,username='chengc',group_id=2),
#     UserProfile(id=2,username='root',group_id=2)
# ])
#
# ss.add_all([
#     Host(id=1,hostname='test',ip_addr='192.168.4.193',port=22),
#     Host(id=2,hostname='zhongrt1',ip_addr='223.202.101.164',port=43228)
# ])
#
# ss.add_all([
#     HostUser(id=1,auth_type='pwd',username='root',password='7ujm8ik,',host_id=1),
#     HostUser(id=2,auth_type='pwd',username='root',password='asdf',host_id=2)
# ])
#
# ss.add_all([
#     UserProfile2HostUser(userprofile_id=1,hostuser_id=1),
#     UserProfile2HostUser(userprofile_id=2,hostuser_id=1)
# ])
# #
# ss.add_all([Group(id=3,name='SB')])
ss.commit()
特别注意:如果使用relationship虚拟关系时,需要添加secondary=类名.table,否则插入数据时会报错!
用paramiko写堡垒机的更多相关文章
- (转)用Python写堡垒机项目
		原文:https://blog.csdn.net/ywq935/article/details/78816860 前言 堡垒机是一种运维安全审计系统.主要的功能是对运维人员的运维操作进行审计和权限控制 ... 
- Paramiko和堡垒机实现
		一.Paramiko paramiko模块,基于SSH用于连接远程服务器并执行相关操作. 1.安装:pip install paramiko 2.SSHClient:用于连接远程服务器并执行基本命令 ... 
- Python paramiko模块 + 堡垒机
		paremiko SSHClient 用于连接远程服务器并执行基本命令 基于用户名密码连接: import paramiko # 创建SSH对象 ssh = paramiko.SSHClient ... 
- paramiko 堡垒机
		用paramiko写堡垒机 paramiko paramiko模块,基于SSH用于连接远程服务器并执行相关操作. 基本用法 SSHClient 基于用户名密码连接: 基础用法: import para ... 
- 堡垒机前戏——paramiko
		提要:在写堡垒机之前,我们必须要了解paramiko这个第三方库.有关于python的第三方库的安装很简单,可以自行百度. 该模块基于SSH用于连接远程服务器并执行相关操作. SSHClient 用于 ... 
- Python自动化运维之19、Paramiko模块和堡垒机实战
		paramiko模块 paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实 ... 
- Python----Paramiko模块和堡垒机实战
		paramiko模块 paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实 ... 
- python2.0 s12 day8 _ 堡垒机前戏paramiko模块
		堡垒机前戏 开发堡垒机之前,先来学习Python的paramiko模块,该模块机遇SSH用于连接远程服务器并执行相关操作 paramiko模块是做主机管理的,他模拟了一个ssh. 有两种形式连接形式, ... 
- 堡垒机(paramiko)
		实现思路 堡垒机执行流程: 管理员为用户在服务器上创建账号(将公钥放置服务器,或者使用用户名密码) 用户登陆堡垒机,输入堡垒机用户名密码,现实当前用户管理的服务器列表 用户选择服务器,并自动登陆 执行 ... 
随机推荐
- vim下的ctags和taglist等的使用和配置
			1.ctags (1)到 http://prdownloads.sourceforge.net/ctags/ctags-5.6.tar.gz 下载ctags源码ctags-5.6.ta ... 
- cordova屏幕尺寸
			<platform name="android"> <!-- ldpi : 36x36 px mdpi : 48x48 px hdpi : 72x72 px xh ... 
- iOS  展示 gif
			gif 图 是多张依次有连续动作的图 顺时间展示的一种动态效果图 . 有的是均匀时间更换下一张 有的 则不是均匀时间变化 1. 那么 对于均匀 时间变化的gif图 比较适合 使用 iOS 系统自 ... 
- 【leetcode刷题笔记】Best Time to Buy and Sell Stock II
			Say you have an array for which the ith element is the price of a given stock on day i. Design an al ... 
- new的越界访问
			今天敲代码的时候发现了一个BUG和大家分享一下,希望大家下次不要犯和我一样的错误. 如果犯了和我一样的错,也能知道自己错在哪里! <(^-^)> 函数如下:(斐波那契数列的实现) lo ... 
- 什么是gitlab CI ?CI代表什么?
			CI是Continuous Integration的简称,就是持续集成的意思. 就是说你代码改动了,测试了,提交了,持续集成系统会自动构建(编译等等).持续集成的理念是每个提交的版本都应该是可交付的, ... 
- ansible普通用户su切换问题
			在现网应用中,安全加固后的主机是不允许直接以root用户登陆的,而很多命令又需要root用户来执行,在不改造现网的情况下.希望通过一个普通用户先登陆,再su切到root执行.而且每台主机的普通用户和r ... 
- java深入探究05
			通讯录小程序 需求说明: 功能: 添加联系人 修改联系人 删除联系人 查询所有联系人 要求: console控制 数据保存在xml 1.创建联系人类 /** * 联系人实体对象 * @author A ... 
- 针对PHP性能方面编程技巧的总结
			注:本文为作者多年工作经验总结,转贴请注明出处 Albert Wang 做PHP已经有4年多了吧,总是没写点什么总结一下,现在开始写博客了,也顺便总结下吧,留给那些新入行的朋友看看. 1. 不宜频繁使 ... 
- java语法糖---枚举
			java语法糖---枚举 在JDK5.0中提供了大量的语法糖,例如:自动装箱拆箱.增强for循环.枚举.泛型等.所谓“语法糖”就是指提供更便利的语法供程序员使用,只是在编译器上做了手脚,却没有提供 ... 
