日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

第十三章:Python の 网络编程进阶(二)

發(fā)布時間:2024/4/15 python 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第十三章:Python の 网络编程进阶(二) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本課主題

  • SQLAlchemy - Core
  • SQLAlchemy - ORM
  • Paramiko?介紹和操作
  • 上下文操作應用
  • 初探堡壘機

?

SQLAlchemy - Core

連接 URL

通過 create_engine 方法創(chuàng)建 MySQL 數(shù)據(jù)庫的連接,create_engine("url") 接受一個 URL 連接:>>> MySQL-Python: mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>>>> pymysql: mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]>>> MySQL-Connector: mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>>>> cx_Oracle: oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]>>> PostgreSQL: postgresql+pg8000://<user>:<password>@<host>/<db>Details: http://docs.sqlalchemy.org/en/latest/dialects/index.html create_engine("url")

用 SQLAlchemy 來創(chuàng)建數(shù)據(jù)庫表

from datetime import datetime# ------------------------------------------------------------ # Import the datatype # ------------------------------------------------------------ from sqlalchemy import (MetaData, Table, Column, Integer, Numeric, String, Float, Boolean,DateTime, ForeignKey, create_engine ) metadata = MetaData()# ------------------------------------------------------------ # Table Objects # ------------------------------------------------------------ # 創(chuàng)建 cookies 的數(shù)據(jù)庫表 cookies = Table('cookies', metadata,Column('cookie_id', Integer(), primary_key=True),Column('cookie_name', String(50), index=True), #創(chuàng)建索引Column('cookie_recipe_url', String(255)),Column('cookie_sku', String(55)),Column('quantity', Integer()),Column('unit_cost', Numeric(12,2))# Index('ix_cookies_cookie_name', 'cookie_name') #也可以調用這個方法來創(chuàng)建索引 )# 創(chuàng)建 users 的數(shù)據(jù)庫表 user = Table('users', metadata,Column('user_id', Integer(), primary_key=True),Column('username', String(15), nullable=False, unique=True),Column('email_address', String(255), nullable=False),Column('phone', String(20), nullable=False),Column('password', String(25), nullable=False),Column('created_on', DateTime(), default=datetime.now),Column('updated_on', DateTime(), default=datetime.now, onupdate=datetime.now)# PrimaryKeyConstraint('user_id', name='user_pk'), #也可以調用這個方法來創(chuàng)建主鍵# UniqueConstraint('username', name='uix_username'), #也可以調用這個方法來創(chuàng)建唯一# CheckConstraint('unit_cost >= 0.00', name='unit_cost_positive') )# 創(chuàng)建 orders 的數(shù)據(jù)庫表 orders = Table('orders', metadata,Column('order_id', Integer(), primary_key=True),Column('user_id', ForeignKey('users.user_id')),Column('shipped', Boolean(), default=False) )# 創(chuàng)建 line_items 的數(shù)據(jù)庫表 line_items = Table('line_items', metadata,Column('line_items_id', Integer(), primary_key=True),Column('order_id', ForeignKey('orders.order_id')),Column('cookie_id', ForeignKey('cookies.cookie_id')),Column('quantity', Integer()),Column('extended_cost', Numeric(12,2)) )# ------------------------------------------------------------ # 連接url # ------------------------------------------------------------ engine = create_engine("mysql+pymysql://root@localhost/demo_db", pool_recycle=3600)# ------------------------------------------------------------ # 調用 create_all() 方法來創(chuàng)建所有數(shù)據(jù)庫表 # 默認該方法不會對已經存在的數(shù)據(jù)庫表重新創(chuàng)建。 # ------------------------------------------------------------ metadata.create_all(engine) metadata.create_all(engine)

???

?

SQLAlchemy - ORM

這是一個叫?Object Relational Mapping 框架,可以讓我們通過類和對象來操作數(shù)據(jù)庫,具體功能包括創(chuàng)建表,定義數(shù)據(jù)類型,新增或者查詢,一般?MySQL 能做的功能,都可以在 SQLALchemy 里實現(xiàn),我也是用上一章的那個數(shù)據(jù)模型去介紹如何用 SQLALchemy 的API去操作數(shù)據(jù)庫。

一對多例子:

從人的角度看:每個人只可以選擇種顏色(一對一);從顏色的角度看:但是一種顏色可以有多個人選擇(一對多)

對多例子

從組的角度看:每個組可以有不同的服務器(一對多);從服務器的角度看:每個服務器也可以屬于不同的組(一對多)

表操作

  • 創(chuàng)建連接
    engine = create_engine('mysql+pymysql://myuser:mypass@192.168.80.128:3306/s13', max_overflow = 5) 創(chuàng)建 MySQL連接
  • 創(chuàng)建表
    Base.metadata.create_all(engine) Base.metadata.create_all( ) #!/usr/bin/env python # -*- coding:utf-8 -*-from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engineengine = create_engine('mysql+pymysql://myuser:mypass@192.168.80.128:3306/s13', max_overflow = 5) # 創(chuàng)建連接 Base = declarative_base()# Many-to-many class Servers_to_Groups(Base):__tablename__ = 'rel_servers_groups'nid = Column(Integer, nullable=False, primary_key=True)server_id = Column(Integer, ForeignKey('servers.id'))group_id = Column(Integer, ForeignKey('groups.id'))# 在 sqlalchemy 支持創(chuàng)建 relationship,方便查詢groups = relationship('Groups', backref = 'lkp_servers') # 在groups表的內部, sqlalchemy 會多創(chuàng)建一個隱性字段名叫 lkp_serversservers = relationship('Servers', backref = 'lkp_groups') # 在servers表的內部, sqlalchemy 會多創(chuàng)建一個隱性字段名叫 lkp_groupsclass Groups(Base):__tablename__ = 'groups'id = Column(Integer, nullable=False, primary_key=True)name = Column(String(50), unique=True, nullable=False)port = Column(Integer, default=22)class Servers(Base):__tablename__ = 'servers'id = Column(Integer, nullable=False, primary_key=True)name = Column(String(64), unique=True, nullable=False) sqlalchemy創(chuàng)建表(多對多) #!/usr/bin/env python # -*- coding:utf-8 -*-from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engineengine = create_engine('mysql+pymysql://myuser:mypass@192.168.80.128:3306/s13', max_overflow = 5) # 創(chuàng)建連接 Base = declarative_base()class Users(Base):__tablename__ = 'users'id = Column(Integer, nullable=False, primary_key=True)name = Column(String(32))extra = Column(String(16))__table_args__ = (UniqueConstraint('id','name', name = 'unix_id_name'),Index('ix_id_name','name','extra'))# One-to-many class Favor(Base):__tablename__ = 'favor'nid = Column(Integer, primary_key=True)caption = Column(String(32), default='red', unique=True)#當打印這個類時,會使用以下方法def __repr__(self):return "%s-%s" %(self.nid, self.caption)class Person(Base):__tablename__ = 'person'nid = Column(Integer, primary_key=True)name = Column(String(32), index=True, nullable=True)favor_id = Column(Integer, ForeignKey('favor.nid'))favor = relationship('favor', backref='lkp_person') # 在favor表的內部,sqlalchemy會多創(chuàng)建一個隱性字段名叫l(wèi)kp_person sqlalchemy創(chuàng)建表(一對多)
  • 刪除表 DROP TABLE
    Base.metadata.drop_all(engine) Base.metadata.drop_all( )
  • 定義自動增量?AUTO INCREMEN/主鍵 PRIMARY KEY
    sid = Column(Integer, primary_key=True, autoincrement=True) autoincrement,primary_key
  • 定義外鍵 FOREIGN KEY
    person_sid = Column(Integer, ForeignKey("dm_person.sid"),nullable=False) ForeignKey( )
  • 定義關系 relationship
    class Favor(Base):__tablename__ = 'favor'nid = Column(Integer, primary_key=True)caption = Column(String(32), default='red', unique=True)#當打印這個類時,會使用以下方法def __repr__(self):return "%s-%s" %(self.nid, self.caption)class Person(Base):__tablename__ = 'person'nid = Column(Integer, primary_key=True)name = Column(String(32), index=True, nullable=True)favor_id = Column(Integer, ForeignKey('favor.nid'))favor = relationship('favor', backref='lkp_person') relationship( )
  • 定義約束?CONSTRAINT
    class Users(Base):__tablename__ = 'users'id = Column(Integer, nullable=False, primary_key=True)name = Column(String(32))extra = Column(String(16))__table_args__ = (UniqueConstraint('id','name', name = 'unix_id_name'),Index('ix_id_name','name','extra')) sqlalchemy中的約束
  • ALTER TABLE
  • WHERE
    # WHERE CLAUSE record = session.query(Users).filter_by(name='janice').all() record = session.query(Users).filter(Users.id > 1, Users.name == 'janice').all() record = session.query(Users).filter(Users.id.between(1,3)).all() record = session.query(Users).filter(Users.id.in_([1,3,4])).all() record = session.query(Users).filter(~Users.id.in_([1,3,4])).all() record = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter_by(name='janice'))).all() session.query( ).filter( )
  • ORDER BY
    record = session.query(Users).order_by(Users.name.desc()).all() record = session.query(Users).order_by(Users.name.desc(), Users.id.asc()).all()for res in record:print(res.name, res.extra)""" ken manager janice engineer alex director """ session.query( ).order_by(User.name.desc())
  • GROUP BY
    record = session.query(func.max(Users.id),func.min(Users.id),func.sum(Users.id) ).group_by(Users.name).having(func.min(Users.id) > 2).all()for res in record:print(res) session.query().group_by()
  • UNION/ UNION ALL
    q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.nid).filter(Favor.nid > 2)# record = q1.union(q2) record = q1.union_all(q2)for res in record.all():print(res) q1.union()/union_all(q2)
  • LIKE
    # record = session.query(Users).filter(Users.name.like('j%')).all() record = session.query(Users).filter(~Users.name.like('j%')).all()for res in record:print(res.name, res.extra) session.query().filter(User.name.like())
  • LIMIT
    record = session.query(Users)[1:2] session.query()[1:2]
  • INSERT
    session.add_all([Favor(caption='red'),Favor(caption='orange'),Favor(caption='yellow'),Favor(caption='green'),Favor(caption='blue'),Favor(caption='purple') ])session.add_all([Person(name='janice',favor_id=4),Person(name='alex',favor_id=1),Person(name='kennith',favor_id=6),Person(name='peter',favor_id=3),Person(name='jennifer',favor_id=2),Person(name='winnie',favor_id=5) ]) session.add()/add_all([])
  • DELETE
    session.query(Users).filter(Users.id > 2).delete() session.query().delete()
  • UPDATE
    session.query(Users).filter(Users.id > 2).update({"name":"updated_kennith"}) session.query(Users).filter(Users.id > 2).update({Users.name: Users.name + "_100"}, synchronize_session=False) session.query(Users).filter(Users.id > 2).update({"id": Users.id + 100}, synchronize_session="evaluate") session.query().update()
  • SELECT
    record = session.query(Users) #可以查看原生 SQL 語句 record = session.query(Users).all() # 返回的是Users object對象 record = session.query(Users.name, Users.extra).all() # 返回的是Users.name 和 Users.extra 的內容,因為Users.name, Users.extra作為參數(shù)傳入了 query() record = session.query(Users).filter_by(name='alex').all() # 返回的是一個可迭代的對象 e.g.for res in record; print(res.name) record = session.query(Users).filter_by(name='alex').first() # 返回的是Users.name 和 Users.extra 的內容 session.query().first()/all()
  • INNER JOIN / LEFT OUTER JOIN
    record = session.query(Favor, Person).filter(Person.favor_id==Favor.nid).all() for res in record:print(res[0], res[1].name)record = session.query(Person.name,Favor.caption).join(Favor).all() record = session.query(Person).join(Favor, isouter=True) print(record) session.query().join()/.join(isouter=True)
  • SUM/ MIN/ MAX
    record = session.query(func.max(Users.id),func.min(Users.id),func.sum(Users.id) ).group_by(Users.name).having(func.min(Users.id) > 2).all()for res in record:print(res)""" (3, 3, Decimal('3')) """ SUM/ MIN/ MAX
  • ?

    數(shù)據(jù)分析例子

    現(xiàn)在我會用我上一章的那個數(shù)據(jù)模型來回答以下問題,這是一個記錄了每個用戶購買了那些貨品的一張數(shù)據(jù)表,還有記錄了購買了多少件數(shù)和每件貨品的價格是多少。


    第一步是在數(shù)據(jù)庫上創(chuàng)建以上的表,分別是產品,人和銷售表。產品表記錄了一些產品名稱和產品類型; 人表記錄了購買人的名稱和年齡;銷售表完整的記錄了誰買了什么、買了多少產品和產品的價格是多少、等等...現(xiàn)在會使用 Python 的 SQLAlchemy 去完成所有的工作,而不是用原生 SQL 去做。

  • 第一步:創(chuàng)建產品,人和銷售表的表
    from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine# 創(chuàng)建連接 engine = create_engine('mysql+pymysql://myuser:mypass@172.16.201.134:3306/s13?',max_overflow = 5,pool_recycle=3600)Base = declarative_base()class Person(Base):__tablename__ = 'dm_person'sid = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(10), unique=True, nullable=False)age = Column(Integer, nullable=False)class Product(Base):__tablename__ = 'dm_product'sid = Column(Integer, primary_key=True, autoincrement=True)product_name = Column(String(50), unique=True, nullable=False)product_category = Column(String(50), nullable=False)class Sales(Base):__tablename__ = 'fct_sales'sid = Column(Integer, primary_key=True, autoincrement=True)person_sid = Column(Integer, ForeignKey("dm_person.sid"),nullable=False)product_sid = Column(Integer, ForeignKey("dm_product.sid"),nullable=False)unit_price = Column(Integer, nullable=True)qty = Column(Integer, nullable=True)person = relationship("Person","lkp_sales")product = relationship("Product","lkp_sales") 創(chuàng)建表ORM模型
  • 第二步:創(chuàng)建一個 session 來進行數(shù)據(jù)操作
    Session = sessionmaker(bind=engine) session = Session() # blablabla... session.commit() Session = sessionmaker(bind=engine)
  • 第三步:插入一些測試數(shù)據(jù),此時,我會調用剛才學的 session.add_all( )方法
    def data_init():# 創(chuàng)建連接Session = sessionmaker(bind=engine)session = Session()# 初始化插入數(shù)據(jù) - Person Table session.add_all([Person(name='janice', age=22),Person(name='alex', age=33),Person(name='ken', age=30),Person(name='peter', age=28),Person(name='david', age=23),Person(name='ziv', age=25),Person(name='ronald', age=21),Person(name='kenny', age=36)])# 初始化插入數(shù)據(jù) - Product Table session.add_all([Product(product_name='iPhone 6S', product_category='Electronic'),Product(product_name='iPhone 7', product_category='Electronic'),Product(product_name='XiaoMi 5', product_category='Electronic'),Product(product_name='Samsung Note 7', product_category='Electronic'),Product(product_name='Programming in Python', product_category='Books'),Product(product_name='Python In Action', product_category='Books'),Product(product_name='Shakespeare', product_category='Books'),Product(product_name='Coconut Water', product_category='Foods and Drinks'),Product(product_name='Coffe', product_category='Foods and Drinks'),Product(product_name='Bike', product_category='Automobile'),Product(product_name='Tesla Model X', product_category='Automobile')])# SQL commit session.commit()data_init() session.add( )/ add_all( )函數(shù)例子
  • 數(shù)據(jù)準備好了,現(xiàn)在可以嘗試回答問題啦!

  • 問題一:我想知道這商店有什么產品,可以調用 session.query( ) 去查詢 print("問題一:我想知道這商店有什么產品?") Session = sessionmaker(bind=engine) session = Session()record = session.query(Product.product_name, Product.product_category).all()print("\n答: 這商店有以下產品:") for row in enumerate(record,1):print('{}. {}'.format(row[0],row[1][0]))# record = session.query(Product).all() # for r in enumerate(record, 1): # print('{}. {}'.format(r[0],r[1].product_name)) session.commit()""" 問題一:我想知道這商店有什么產品?答: 這商店有以下產品: 1. iPhone 6S 2. iPhone 7 3. XiaoMi 5 4. Samsung Note 7 5. Programming in Python 6. Python In Action 7. Shakespeare 8. Coconut Water 9. Coffe 10. Bike 11. Tesla Model X """ session.query( )函數(shù)例子
  • 問題二:我想知道這商店有什么"電子產品"可以賣 (提示:Electronic)?
    print("問題二:我想知道這商店有什么'電子產品'可以賣") Session = sessionmaker(bind=engine) session = Session()# record = session.query(Product).filter_by(product_category = 'Electronic').all() record = session.query(Product).filter(Product.product_category == 'Electronic').all()print("\n答: 這商店有以下電子產品:") for row in enumerate(record,1):print('{}. {}'.format(row[0],row[1].product_name))session.commit()""" 問題二:我想知道這商店有什么'電子產品'可以賣答: 這商店有以下電子產品: 1. iPhone 6S 2. iPhone 7 3. XiaoMi 5 4. Samsung Note 7 """ session.query( ).filter( )/ filter_by( )函數(shù)例子
  • 問題三:我想知道 janice 買了什么東西
    print("問題三:我想知道 janice 買了什么東西") Session = sessionmaker(bind=engine) session = Session()#method1 record = session.query(Product.product_name).filter(Sales.person_sid==Person.sid, Sales.product_sid==Product.sid, Person.name == 'janice').all()print("\n答: janice 買了以下東西:") for row in enumerate(record,1):print('{}. {}'.format(row[0],row[1].product_name))session.commit()""" 問題三:我想知道 janice 買了什么東西答: janice 買了以下東西: 1. Shakespeare 2. Coffe 3. Samsung Note 7 4. Tesla Model X 5. iPhone 7 6. XiaoMi 5 7. Coconut Water 8. Python In Action """ 表關聯(lián)和filter( )函數(shù)例子
  • 問題四:我想知道 janice 總共花費了多少錢
    print("問題四:我想知道 janice 總共花費了多少錢") Session = sessionmaker(bind=engine) session = Session()from sqlalchemy.sql import funcrecord = session.query(func.sum(Sales.unit_price * Sales.qty) ).filter(Sales.person_sid==Person.sid,Sales.product_sid==Product.sid,Person.name == 'janice').group_by(Person.name).all()print("\n答: janice 總共花費了 ${}".format(record[0][0]))session.commit() """ 問題四:我想知道 janice 總共花費了多少錢 答: janice 總共花費了 $880595""" 表關聯(lián), sum(), group_by()和filter( )函數(shù)例子
  • 問題五:我想知道每個用戶總共花費了多少錢,以花費最多的排序
    print("問題五:我想知道每個用戶總共花費了多少錢,以花費最多的排序") Session = sessionmaker(bind=engine) session = Session()from sqlalchemy.sql import funcrecord = session.query(Person.name,func.sum(Sales.unit_price * Sales.qty) ).filter(Sales.person_sid==Person.sid,Sales.product_sid==Product.sid).group_by(Person.name).order_by(func.sum(Sales.unit_price * Sales.qty).desc()).all()for row in enumerate(record,1):print('第{}名: {},總共花費了 ${}元'.format(row[0],row[1][0],row[1][1]))session.commit()""" 問題五:我想知道每個用戶總共花費了多少錢,以花費最多的排序第1名: janice,總共花費了 $880595元 第2名: peter,總共花費了 $820101元 第3名: alex,總共花費了 $97684元 第4名: ken,總共花費了 $97126元 """ 表關聯(lián), sum(), group_by(), order_by()和filter( )函數(shù)例子
  • 問題六:我想知道誰買了 Tesla 又買了一個小米手機
    print("問題六:我想知道誰買了 Tesla 又買了一個小米手機") Session = sessionmaker(bind=engine) session = Session()from sqlalchemy import and_, or_, distinctrecord = session.query(distinct(Person.name,)).\filter(Sales.person_sid==Person.sid,Sales.product_sid==Product.sid).\filter(Product.product_name.in_(['Tesla Model X','XiaoMi 5'])).all()print("買了 Tesla 又買了一個小米手機是:") ret = [row[0] for row in record] print(ret)""" 問題六:我想知道誰買了 Tesla 又買了一個小米手機 買了 Tesla 又買了一個小米手機是: ['janice', 'peter', 'ken'] """ 表關聯(lián),in_(), distinct()
  • ?

    Paramiko?介紹和操作

    paramiko模塊,基于SSH用于連接遠程服務器并執(zhí)行相關操作,先給大家有一個大概的概念,Paramiko 可支持以下兩種登入方法和創(chuàng)建三種不同的對象來完成工作:

  • 創(chuàng)建 SSHClient e.g. paramiko.SSHClient( ) - 用于連接遠程服務器并執(zhí)行基本命令
    class SSHClient (ClosingContextManager):"""A high-level representation of a session with an SSH server. This classwraps `.Transport`, `.Channel`, and `.SFTPClient` to take care of mostaspects of authenticating and opening channels. A typical use case is::client = SSHClient()client.load_system_host_keys()client.connect('ssh.example.com')stdin, stdout, stderr = client.exec_command('ls -l')You may pass in explicit overrides for authentication and server host keychecking. The default mechanism is to try to use local key files or anSSH agent (if one is running).Instances of this class may be used as context managers... versionadded:: 1.6"""def __init__(self):"""Create a new SSHClient."""self._system_host_keys = HostKeys()self._host_keys = HostKeys()self._host_keys_filename = Noneself._log_channel = Noneself._policy = RejectPolicy()self._transport = Noneself._agent = Nonedef load_system_host_keys(self, filename=None):"""Load host keys from a system (read-only) file. Host keys read withthis method will not be saved back by `save_host_keys`.This method can be called multiple times. Each new set of host keyswill be merged with the existing set (new replacing old if there areconflicts).If ``filename`` is left as ``None``, an attempt will be made to readkeys from the user's local "known hosts" file, as used by OpenSSH,and no exception will be raised if the file can't be read. This isprobably only useful on posix.:param str filename: the filename to read, or ``None``:raises IOError:if a filename was provided and the file could not be read"""if filename is None:# try the user's .ssh key file, and mask exceptionsfilename = os.path.expanduser('~/.ssh/known_hosts')try:self._system_host_keys.load(filename)except IOError:passreturnself._system_host_keys.load(filename)def load_host_keys(self, filename):"""Load host keys from a local host-key file. Host keys read with thismethod will be checked after keys loaded via `load_system_host_keys`,but will be saved back by `save_host_keys` (so they can be modified).The missing host key policy `.AutoAddPolicy` adds keys to this set andsaves them, when connecting to a previously-unknown server.This method can be called multiple times. Each new set of host keyswill be merged with the existing set (new replacing old if there areconflicts). When automatically saving, the last hostname is used.:param str filename: the filename to read:raises IOError: if the filename could not be read"""self._host_keys_filename = filenameself._host_keys.load(filename)def save_host_keys(self, filename):"""Save the host keys back to a file. Only the host keys loaded with`load_host_keys` (plus any added directly) will be saved -- not anyhost keys loaded with `load_system_host_keys`.:param str filename: the filename to save to:raises IOError: if the file could not be written"""# update local host keys from file (in case other SSH clients# have written to the known_hosts file meanwhile.if self._host_keys_filename is not None:self.load_host_keys(self._host_keys_filename)with open(filename, 'w') as f:for hostname, keys in self._host_keys.items():for keytype, key in keys.items():f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))def get_host_keys(self):"""Get the local `.HostKeys` object. This can be used to examine thelocal host keys or change them.:return: the local host keys as a `.HostKeys` object."""return self._host_keysdef set_log_channel(self, name):"""Set the channel for logging. The default is ``"paramiko.transport"``but it can be set to anything you want.:param str name: new channel name for logging"""self._log_channel = namedef set_missing_host_key_policy(self, policy):"""Set policy to use when connecting to servers without a known host key.Specifically:* A **policy** is an instance of a "policy class", namely some subclassof `.MissingHostKeyPolicy` such as `.RejectPolicy` (the default),`.AutoAddPolicy`, `.WarningPolicy`, or a user-created subclass... note::This method takes class **instances**, not **classes** themselves.Thus it must be called as e.g.``.set_missing_host_key_policy(WarningPolicy())`` and *not*``.set_missing_host_key_policy(WarningPolicy)``.* A host key is **known** when it appears in the client object's cachedhost keys structures (those manipulated by `load_system_host_keys`and/or `load_host_keys`).:param .MissingHostKeyPolicy policy:the policy to use when receiving a host key from apreviously-unknown server"""self._policy = policydef _families_and_addresses(self, hostname, port):"""Yield pairs of address families and addresses to try for connecting.:param str hostname: the server to connect to:param int port: the server port to connect to:returns: Yields an iterable of ``(family, address)`` tuples"""guess = Trueaddrinfos = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM)for (family, socktype, proto, canonname, sockaddr) in addrinfos:if socktype == socket.SOCK_STREAM:yield family, sockaddrguess = False# some OS like AIX don't indicate SOCK_STREAM support, so just guess. :(# We only do this if we did not get a single result marked as socktype == SOCK_STREAM.if guess:for family, _, _, _, sockaddr in addrinfos:yield family, sockaddrdef connect(self,hostname,port=SSH_PORT,username=None,password=None,pkey=None,key_filename=None,timeout=None,allow_agent=True,look_for_keys=True,compress=False,sock=None,gss_auth=False,gss_kex=False,gss_deleg_creds=True,gss_host=None,banner_timeout=None):"""Connect to an SSH server and authenticate to it. The server's host keyis checked against the system host keys (see `load_system_host_keys`)and any local host keys (`load_host_keys`). If the server's hostnameis not found in either set of host keys, the missing host key policyis used (see `set_missing_host_key_policy`). The default policy isto reject the key and raise an `.SSHException`.Authentication is attempted in the following order of priority:- The ``pkey`` or ``key_filename`` passed in (if any)- Any key we can find through an SSH agent- Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in``~/.ssh/``- Plain username/password auth, if a password was givenIf a private key requires a password to unlock it, and a password ispassed in, that password will be used to attempt to unlock the key.:param str hostname: the server to connect to:param int port: the server port to connect to:param str username:the username to authenticate as (defaults to the current localusername):param str password:a password to use for authentication or for unlocking a private key:param .PKey pkey: an optional private key to use for authentication:param str key_filename:the filename, or list of filenames, of optional private key(s) totry for authentication:param float timeout:an optional timeout (in seconds) for the TCP connect:param bool allow_agent:set to False to disable connecting to the SSH agent:param bool look_for_keys:set to False to disable searching for discoverable private keyfiles in ``~/.ssh/``:param bool compress: set to True to turn on compression:param socket sock:an open socket or socket-like object (such as a `.Channel`) to usefor communication to the target host:param bool gss_auth:``True`` if you want to use GSS-API authentication:param bool gss_kex:Perform GSS-API Key Exchange and user authentication:param bool gss_deleg_creds: Delegate GSS-API client credentials or not:param str gss_host:The targets name in the kerberos database. default: hostname:param float banner_timeout: an optional timeout (in seconds) to waitfor the SSH banner to be presented.:raises BadHostKeyException: if the server's host key could not beverified:raises AuthenticationException: if authentication failed:raises SSHException: if there was any other error connecting orestablishing an SSH session:raises socket.error: if a socket error occurred while connecting.. versionchanged:: 1.15Added the ``banner_timeout``, ``gss_auth``, ``gss_kex``,``gss_deleg_creds`` and ``gss_host`` arguments."""if not sock:errors = {}# Try multiple possible address families (e.g. IPv4 vs IPv6)to_try = list(self._families_and_addresses(hostname, port))for af, addr in to_try:try:sock = socket.socket(af, socket.SOCK_STREAM)if timeout is not None:try:sock.settimeout(timeout)except:passretry_on_signal(lambda: sock.connect(addr))# Break out of the loop on successbreakexcept socket.error as e:# Raise anything that isn't a straight up connection error# (such as a resolution error)if e.errno not in (ECONNREFUSED, EHOSTUNREACH):raise# Capture anything else so we know how the run looks once# iteration is complete. Retain info about which attempt# this was.errors[addr] = e# Make sure we explode usefully if no address family attempts# succeeded. We've no way of knowing which error is the "right"# one, so we construct a hybrid exception containing all the real# ones, of a subclass that client code should still be watching for# (socket.error)if len(errors) == len(to_try):raise NoValidConnectionsError(errors)t = self._transport = Transport(sock, gss_kex=gss_kex, gss_deleg_creds=gss_deleg_creds)t.use_compression(compress=compress)if gss_kex and gss_host is None:t.set_gss_host(hostname)elif gss_kex and gss_host is not None:t.set_gss_host(gss_host)else:passif self._log_channel is not None:t.set_log_channel(self._log_channel)if banner_timeout is not None:t.banner_timeout = banner_timeoutt.start_client()ResourceManager.register(self, t)server_key = t.get_remote_server_key()keytype = server_key.get_name()if port == SSH_PORT:server_hostkey_name = hostnameelse:server_hostkey_name = "[%s]:%d" % (hostname, port)# If GSS-API Key Exchange is performed we are not required to check the# host key, because the host is authenticated via GSS-API / SSPI as# well as our client.if not self._transport.use_gss_kex:our_server_key = self._system_host_keys.get(server_hostkey_name,{}).get(keytype, None)if our_server_key is None:our_server_key = self._host_keys.get(server_hostkey_name,{}).get(keytype, None)if our_server_key is None:# will raise exception if the key is rejected; let that fall out self._policy.missing_host_key(self, server_hostkey_name,server_key)# if the callback returns, assume the key is okour_server_key = server_keyif server_key != our_server_key:raise BadHostKeyException(hostname, server_key, our_server_key)if username is None:username = getpass.getuser()if key_filename is None:key_filenames = []elif isinstance(key_filename, string_types):key_filenames = [key_filename]else:key_filenames = key_filenameif gss_host is None:gss_host = hostnameself._auth(username, password, pkey, key_filenames, allow_agent,look_for_keys, gss_auth, gss_kex, gss_deleg_creds, gss_host)def close(self):"""Close this SSHClient and its underlying `.Transport`... warning::Failure to do this may, in some situations, cause your Pythoninterpreter to hang at shutdown (often due to race conditions).It's good practice to `close` your client objects anytime you'redone using them, instead of relying on garbage collection."""if self._transport is None:returnself._transport.close()self._transport = Noneif self._agent is not None:self._agent.close()self._agent = Nonedef exec_command(self, command, bufsize=-1, timeout=None, get_pty=False):"""Execute a command on the SSH server. A new `.Channel` is opened andthe requested command is executed. The command's input and outputstreams are returned as Python ``file``-like objects representingstdin, stdout, and stderr.:param str command: the command to execute:param int bufsize:interpreted the same way as by the built-in ``file()`` function inPython:param int timeout:set command's channel timeout. See `Channel.settimeout`.settimeout:return:the stdin, stdout, and stderr of the executing command, as a3-tuple:raises SSHException: if the server fails to execute the command"""chan = self._transport.open_session(timeout=timeout)if get_pty:chan.get_pty()chan.settimeout(timeout)chan.exec_command(command)stdin = chan.makefile('wb', bufsize)stdout = chan.makefile('r', bufsize)stderr = chan.makefile_stderr('r', bufsize)return stdin, stdout, stderrdef invoke_shell(self, term='vt100', width=80, height=24, width_pixels=0,height_pixels=0):"""Start an interactive shell session on the SSH server. A new `.Channel`is opened and connected to a pseudo-terminal using the requestedterminal type and size.:param str term:the terminal type to emulate (for example, ``"vt100"``):param int width: the width (in characters) of the terminal window:param int height: the height (in characters) of the terminal window:param int width_pixels: the width (in pixels) of the terminal window:param int height_pixels: the height (in pixels) of the terminal window:return: a new `.Channel` connected to the remote shell:raises SSHException: if the server fails to invoke a shell"""chan = self._transport.open_session()chan.get_pty(term, width, height, width_pixels, height_pixels)chan.invoke_shell()return chandef open_sftp(self):"""Open an SFTP session on the SSH server.:return: a new `.SFTPClient` session object"""return self._transport.open_sftp_client()def get_transport(self):"""Return the underlying `.Transport` object for this SSH connection.This can be used to perform lower-level tasks, like opening specifickinds of channels.:return: the `.Transport` for this connection"""return self._transportdef _auth(self, username, password, pkey, key_filenames, allow_agent,look_for_keys, gss_auth, gss_kex, gss_deleg_creds, gss_host):"""Try, in order:- The key passed in, if one was passed in.- Any key we can find through an SSH agent (if allowed).- Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in ~/.ssh/(if allowed).- Plain username/password auth, if a password was given.(The password might be needed to unlock a private key, or fortwo-factor authentication [for which it is required].)"""saved_exception = Nonetwo_factor = Falseallowed_types = set()two_factor_types = set(['keyboard-interactive','password'])# If GSS-API support and GSS-PI Key Exchange was performed, we attempt# authentication with gssapi-keyex.if gss_kex and self._transport.gss_kex_used:try:self._transport.auth_gssapi_keyex(username)returnexcept Exception as e:saved_exception = e# Try GSS-API authentication (gssapi-with-mic) only if GSS-API Key# Exchange is not performed, because if we use GSS-API for the key# exchange, there is already a fully established GSS-API context, so# why should we do that again?if gss_auth:try:self._transport.auth_gssapi_with_mic(username, gss_host,gss_deleg_creds)returnexcept Exception as e:saved_exception = eif pkey is not None:try:self._log(DEBUG, 'Trying SSH key %s' % hexlify(pkey.get_fingerprint()))allowed_types = set(self._transport.auth_publickey(username, pkey))two_factor = (allowed_types & two_factor_types)if not two_factor:returnexcept SSHException as e:saved_exception = eif not two_factor:for key_filename in key_filenames:for pkey_class in (RSAKey, DSSKey, ECDSAKey):try:key = pkey_class.from_private_key_file(key_filename, password)self._log(DEBUG, 'Trying key %s from %s' % (hexlify(key.get_fingerprint()), key_filename))allowed_types = set(self._transport.auth_publickey(username, key))two_factor = (allowed_types & two_factor_types)if not two_factor:returnbreakexcept SSHException as e:saved_exception = eif not two_factor and allow_agent:if self._agent is None:self._agent = Agent()for key in self._agent.get_keys():try:self._log(DEBUG, 'Trying SSH agent key %s' % hexlify(key.get_fingerprint()))# for 2-factor auth a successfully auth'd key password will return an allowed 2fac auth methodallowed_types = set(self._transport.auth_publickey(username, key))two_factor = (allowed_types & two_factor_types)if not two_factor:returnbreakexcept SSHException as e:saved_exception = eif not two_factor:keyfiles = []rsa_key = os.path.expanduser('~/.ssh/id_rsa')dsa_key = os.path.expanduser('~/.ssh/id_dsa')ecdsa_key = os.path.expanduser('~/.ssh/id_ecdsa')if os.path.isfile(rsa_key):keyfiles.append((RSAKey, rsa_key))if os.path.isfile(dsa_key):keyfiles.append((DSSKey, dsa_key))if os.path.isfile(ecdsa_key):keyfiles.append((ECDSAKey, ecdsa_key))# look in ~/ssh/ for windows users:rsa_key = os.path.expanduser('~/ssh/id_rsa')dsa_key = os.path.expanduser('~/ssh/id_dsa')ecdsa_key = os.path.expanduser('~/ssh/id_ecdsa')if os.path.isfile(rsa_key):keyfiles.append((RSAKey, rsa_key))if os.path.isfile(dsa_key):keyfiles.append((DSSKey, dsa_key))if os.path.isfile(ecdsa_key):keyfiles.append((ECDSAKey, ecdsa_key))if not look_for_keys:keyfiles = []for pkey_class, filename in keyfiles:try:key = pkey_class.from_private_key_file(filename, password)self._log(DEBUG, 'Trying discovered key %s in %s' % (hexlify(key.get_fingerprint()), filename))# for 2-factor auth a successfully auth'd key will result in ['password']allowed_types = set(self._transport.auth_publickey(username, key))two_factor = (allowed_types & two_factor_types)if not two_factor:returnbreakexcept (SSHException, IOError) as e:saved_exception = eif password is not None:try:self._transport.auth_password(username, password)returnexcept SSHException as e:saved_exception = eelif two_factor:try:self._transport.auth_interactive_dumb(username)returnexcept SSHException as e:saved_exception = e# if we got an auth-failed exception earlier, re-raise itif saved_exception is not None:raise saved_exceptionraise SSHException('No authentication methods available')def _log(self, level, msg):self._transport._log(level, msg) class SSHClient( )源碼
    • 基于用戶名密碼連接
    • 基于公鑰密鑰連接
  • 創(chuàng)建 SFTPClient e.g. paramiko.SFTPClient.from_transport(transport)?- 用于連接遠程服務器并執(zhí)行上傳下載
    class SFTPClient(BaseSFTP, ClosingContextManager):"""SFTP client object.Used to open an SFTP session across an open SSH `.Transport` and performremote file operations.Instances of this class may be used as context managers."""def __init__(self, sock):"""Create an SFTP client from an existing `.Channel`. The channelshould already have requested the ``"sftp"`` subsystem.An alternate way to create an SFTP client context is by using`from_transport`.:param .Channel sock: an open `.Channel` using the ``"sftp"`` subsystem:raises SSHException: if there's an exception while negotiatingsftp"""BaseSFTP.__init__(self)self.sock = sockself.ultra_debug = Falseself.request_number = 1# lock for request_numberself._lock = threading.Lock()self._cwd = None# request # -> SFTPFileself._expecting = weakref.WeakValueDictionary()if type(sock) is Channel:# override default loggertransport = self.sock.get_transport()self.logger = util.get_logger(transport.get_log_channel() + '.sftp')self.ultra_debug = transport.get_hexdump()try:server_version = self._send_version()except EOFError:raise SSHException('EOF during negotiation')self._log(INFO, 'Opened sftp connection (server version %d)' % server_version)@classmethoddef from_transport(cls, t, window_size=None, max_packet_size=None):"""Create an SFTP client channel from an open `.Transport`.Setting the window and packet sizes might affect the transfer speed.The default settings in the `.Transport` class are the same as inOpenSSH and should work adequately for both files transfers andinteractive sessions.:param .Transport t: an open `.Transport` which is already authenticated:param int window_size:optional window size for the `.SFTPClient` session.:param int max_packet_size:optional max packet size for the `.SFTPClient` session..:return:a new `.SFTPClient` object, referring to an sftp session (channel)across the transport.. versionchanged:: 1.15Added the ``window_size`` and ``max_packet_size`` arguments."""chan = t.open_session(window_size=window_size,max_packet_size=max_packet_size)if chan is None:return Nonechan.invoke_subsystem('sftp')return cls(chan)def _log(self, level, msg, *args):if isinstance(msg, list):for m in msg:self._log(level, m, *args)else:# escape '%' in msg (they could come from file or directory names) before loggingmsg = msg.replace('%','%%')super(SFTPClient, self)._log(level, "[chan %s] " + msg, *([self.sock.get_name()] + list(args)))def close(self):"""Close the SFTP session and its underlying channel... versionadded:: 1.4"""self._log(INFO, 'sftp session closed.')self.sock.close()def get_channel(self):"""Return the underlying `.Channel` object for this SFTP session. Thismight be useful for doing things like setting a timeout on the channel... versionadded:: 1.7.1"""return self.sockdef listdir(self, path='.'):"""Return a list containing the names of the entries in the given ``path``.The list is in arbitrary order. It does not include the specialentries ``'.'`` and ``'..'`` even if they are present in the folder.This method is meant to mirror ``os.listdir`` as closely as possible.For a list of full `.SFTPAttributes` objects, see `listdir_attr`.:param str path: path to list (defaults to ``'.'``)"""return [f.filename for f in self.listdir_attr(path)]def listdir_attr(self, path='.'):"""Return a list containing `.SFTPAttributes` objects corresponding tofiles in the given ``path``. The list is in arbitrary order. It doesnot include the special entries ``'.'`` and ``'..'`` even if they arepresent in the folder.The returned `.SFTPAttributes` objects will each have an additionalfield: ``longname``, which may contain a formatted string of the file'sattributes, in unix format. The content of this string will probablydepend on the SFTP server implementation.:param str path: path to list (defaults to ``'.'``):return: list of `.SFTPAttributes` objects.. versionadded:: 1.2"""path = self._adjust_cwd(path)self._log(DEBUG, 'listdir(%r)' % path)t, msg = self._request(CMD_OPENDIR, path)if t != CMD_HANDLE:raise SFTPError('Expected handle')handle = msg.get_binary()filelist = []while True:try:t, msg = self._request(CMD_READDIR, handle)except EOFError:# done with handlebreakif t != CMD_NAME:raise SFTPError('Expected name response')count = msg.get_int()for i in range(count):filename = msg.get_text()longname = msg.get_text()attr = SFTPAttributes._from_msg(msg, filename, longname)if (filename != '.') and (filename != '..'):filelist.append(attr)self._request(CMD_CLOSE, handle)return filelistdef listdir_iter(self, path='.', read_aheads=50):"""Generator version of `.listdir_attr`.See the API docs for `.listdir_attr` for overall details.This function adds one more kwarg on top of `.listdir_attr`:``read_aheads``, an integer controlling how many``SSH_FXP_READDIR`` requests are made to the server. The default of 50should suffice for most file listings as each request/response cyclemay contain multiple files (dependant on server implementation.).. versionadded:: 1.15"""path = self._adjust_cwd(path)self._log(DEBUG, 'listdir(%r)' % path)t, msg = self._request(CMD_OPENDIR, path)if t != CMD_HANDLE:raise SFTPError('Expected handle')handle = msg.get_string()nums = list()while True:try:# Send out a bunch of readdir requests so that we can read the# responses later on Section 6.7 of the SSH file transfer RFC# explains this# http://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txtfor i in range(read_aheads):num = self._async_request(type(None), CMD_READDIR, handle)nums.append(num)# For each of our sent requests# Read and parse the corresponding packets# If we're at the end of our queued requests, then fire off# some more requests# Exit the loop when we've reached the end of the directory# handlefor num in nums:t, pkt_data = self._read_packet()msg = Message(pkt_data)new_num = msg.get_int()if num == new_num:if t == CMD_STATUS:self._convert_status(msg)count = msg.get_int()for i in range(count):filename = msg.get_text()longname = msg.get_text()attr = SFTPAttributes._from_msg(msg, filename, longname)if (filename != '.') and (filename != '..'):yield attr# If we've hit the end of our queued requests, reset nums.nums = list()except EOFError:self._request(CMD_CLOSE, handle)returndef open(self, filename, mode='r', bufsize=-1):"""Open a file on the remote server. The arguments are the same as forPython's built-in `python:file` (aka `python:open`). A file-likeobject is returned, which closely mimics the behavior of a normalPython file object, including the ability to be used as a contextmanager.The mode indicates how the file is to be opened: ``'r'`` for reading,``'w'`` for writing (truncating an existing file), ``'a'`` forappending, ``'r+'`` for reading/writing, ``'w+'`` for reading/writing(truncating an existing file), ``'a+'`` for reading/appending. ThePython ``'b'`` flag is ignored, since SSH treats all files as binary.The ``'U'`` flag is supported in a compatible way.Since 1.5.2, an ``'x'`` flag indicates that the operation should onlysucceed if the file was created and did not previously exist. This hasno direct mapping to Python's file flags, but is commonly known as the``O_EXCL`` flag in posix.The file will be buffered in standard Python style by default, butcan be altered with the ``bufsize`` parameter. ``0`` turns offbuffering, ``1`` uses line buffering, and any number greater than 1(``>1``) uses that specific buffer size.:param str filename: name of the file to open:param str mode: mode (Python-style) to open in:param int bufsize: desired buffering (-1 = default buffer size):return: an `.SFTPFile` object representing the open file:raises IOError: if the file could not be opened."""filename = self._adjust_cwd(filename)self._log(DEBUG, 'open(%r, %r)' % (filename, mode))imode = 0if ('r' in mode) or ('+' in mode):imode |= SFTP_FLAG_READif ('w' in mode) or ('+' in mode) or ('a' in mode):imode |= SFTP_FLAG_WRITEif 'w' in mode:imode |= SFTP_FLAG_CREATE | SFTP_FLAG_TRUNCif 'a' in mode:imode |= SFTP_FLAG_CREATE | SFTP_FLAG_APPENDif 'x' in mode:imode |= SFTP_FLAG_CREATE | SFTP_FLAG_EXCLattrblock = SFTPAttributes()t, msg = self._request(CMD_OPEN, filename, imode, attrblock)if t != CMD_HANDLE:raise SFTPError('Expected handle')handle = msg.get_binary()self._log(DEBUG, 'open(%r, %r) -> %s' % (filename, mode, hexlify(handle)))return SFTPFile(self, handle, mode, bufsize)# Python continues to vacillate about "open" vs "file"...file = opendef remove(self, path):"""Remove the file at the given path. This only works on files; forremoving folders (directories), use `rmdir`.:param str path: path (absolute or relative) of the file to remove:raises IOError: if the path refers to a folder (directory)"""path = self._adjust_cwd(path)self._log(DEBUG, 'remove(%r)' % path)self._request(CMD_REMOVE, path)unlink = removedef rename(self, oldpath, newpath):"""Rename a file or folder from ``oldpath`` to ``newpath``.:param str oldpath: existing name of the file or folder:param str newpath: new name for the file or folder:raises IOError: if ``newpath`` is a folder, or something else goeswrong"""oldpath = self._adjust_cwd(oldpath)newpath = self._adjust_cwd(newpath)self._log(DEBUG, 'rename(%r, %r)' % (oldpath, newpath))self._request(CMD_RENAME, oldpath, newpath)def mkdir(self, path, mode=o777):"""Create a folder (directory) named ``path`` with numeric mode ``mode``.The default mode is 0777 (octal). On some systems, mode is ignored.Where it is used, the current umask value is first masked out.:param str path: name of the folder to create:param int mode: permissions (posix-style) for the newly-created folder"""path = self._adjust_cwd(path)self._log(DEBUG, 'mkdir(%r, %r)' % (path, mode))attr = SFTPAttributes()attr.st_mode = modeself._request(CMD_MKDIR, path, attr)def rmdir(self, path):"""Remove the folder named ``path``.:param str path: name of the folder to remove"""path = self._adjust_cwd(path)self._log(DEBUG, 'rmdir(%r)' % path)self._request(CMD_RMDIR, path)def stat(self, path):"""Retrieve information about a file on the remote system. The returnvalue is an object whose attributes correspond to the attributes ofPython's ``stat`` structure as returned by ``os.stat``, except that itcontains fewer fields. An SFTP server may return as much or as littleinfo as it wants, so the results may vary from server to server.Unlike a Python `python:stat` object, the result may not be accessed asa tuple. This is mostly due to the author's slack factor.The fields supported are: ``st_mode``, ``st_size``, ``st_uid``,``st_gid``, ``st_atime``, and ``st_mtime``.:param str path: the filename to stat:return:an `.SFTPAttributes` object containing attributes about the givenfile"""path = self._adjust_cwd(path)self._log(DEBUG, 'stat(%r)' % path)t, msg = self._request(CMD_STAT, path)if t != CMD_ATTRS:raise SFTPError('Expected attributes')return SFTPAttributes._from_msg(msg)def lstat(self, path):"""Retrieve information about a file on the remote system, withoutfollowing symbolic links (shortcuts). This otherwise behaves exactlythe same as `stat`.:param str path: the filename to stat:return:an `.SFTPAttributes` object containing attributes about the givenfile"""path = self._adjust_cwd(path)self._log(DEBUG, 'lstat(%r)' % path)t, msg = self._request(CMD_LSTAT, path)if t != CMD_ATTRS:raise SFTPError('Expected attributes')return SFTPAttributes._from_msg(msg)def symlink(self, source, dest):"""Create a symbolic link (shortcut) of the ``source`` path at``destination``.:param str source: path of the original file:param str dest: path of the newly created symlink"""dest = self._adjust_cwd(dest)self._log(DEBUG, 'symlink(%r, %r)' % (source, dest))source = bytestring(source)self._request(CMD_SYMLINK, source, dest)def chmod(self, path, mode):"""Change the mode (permissions) of a file. The permissions areunix-style and identical to those used by Python's `os.chmod`function.:param str path: path of the file to change the permissions of:param int mode: new permissions"""path = self._adjust_cwd(path)self._log(DEBUG, 'chmod(%r, %r)' % (path, mode))attr = SFTPAttributes()attr.st_mode = modeself._request(CMD_SETSTAT, path, attr)def chown(self, path, uid, gid):"""Change the owner (``uid``) and group (``gid``) of a file. As withPython's `os.chown` function, you must pass both arguments, so if youonly want to change one, use `stat` first to retrieve the currentowner and group.:param str path: path of the file to change the owner and group of:param int uid: new owner's uid:param int gid: new group id"""path = self._adjust_cwd(path)self._log(DEBUG, 'chown(%r, %r, %r)' % (path, uid, gid))attr = SFTPAttributes()attr.st_uid, attr.st_gid = uid, gidself._request(CMD_SETSTAT, path, attr)def utime(self, path, times):"""Set the access and modified times of the file specified by ``path``. If``times`` is ``None``, then the file's access and modified times are setto the current time. Otherwise, ``times`` must be a 2-tuple of numbers,of the form ``(atime, mtime)``, which is used to set the access andmodified times, respectively. This bizarre API is mimicked from Pythonfor the sake of consistency -- I apologize.:param str path: path of the file to modify:param tuple times:``None`` or a tuple of (access time, modified time) in standardinternet epoch time (seconds since 01 January 1970 GMT)"""path = self._adjust_cwd(path)if times is None:times = (time.time(), time.time())self._log(DEBUG, 'utime(%r, %r)' % (path, times))attr = SFTPAttributes()attr.st_atime, attr.st_mtime = timesself._request(CMD_SETSTAT, path, attr)def truncate(self, path, size):"""Change the size of the file specified by ``path``. This usuallyextends or shrinks the size of the file, just like the `~file.truncate`method on Python file objects.:param str path: path of the file to modify:param size: the new size of the file:type size: int or long"""path = self._adjust_cwd(path)self._log(DEBUG, 'truncate(%r, %r)' % (path, size))attr = SFTPAttributes()attr.st_size = sizeself._request(CMD_SETSTAT, path, attr)def readlink(self, path):"""Return the target of a symbolic link (shortcut). You can use`symlink` to create these. The result may be either an absolute orrelative pathname.:param str path: path of the symbolic link file:return: target path, as a `str`"""path = self._adjust_cwd(path)self._log(DEBUG, 'readlink(%r)' % path)t, msg = self._request(CMD_READLINK, path)if t != CMD_NAME:raise SFTPError('Expected name response')count = msg.get_int()if count == 0:return Noneif count != 1:raise SFTPError('Readlink returned %d results' % count)return _to_unicode(msg.get_string())def normalize(self, path):"""Return the normalized path (on the server) of a given path. Thiscan be used to quickly resolve symbolic links or determine what theserver is considering to be the "current folder" (by passing ``'.'``as ``path``).:param str path: path to be normalized:return: normalized form of the given path (as a `str`):raises IOError: if the path can't be resolved on the server"""path = self._adjust_cwd(path)self._log(DEBUG, 'normalize(%r)' % path)t, msg = self._request(CMD_REALPATH, path)if t != CMD_NAME:raise SFTPError('Expected name response')count = msg.get_int()if count != 1:raise SFTPError('Realpath returned %d results' % count)return msg.get_text()def chdir(self, path=None):"""Change the "current directory" of this SFTP session. Since SFTPdoesn't really have the concept of a current working directory, this isemulated by Paramiko. Once you use this method to set a workingdirectory, all operations on this `.SFTPClient` object will be relativeto that path. You can pass in ``None`` to stop using a current workingdirectory.:param str path: new current working directory:raises IOError: if the requested path doesn't exist on the server.. versionadded:: 1.4"""if path is None:self._cwd = Nonereturnif not stat.S_ISDIR(self.stat(path).st_mode):raise SFTPError(errno.ENOTDIR, "%s: %s" % (os.strerror(errno.ENOTDIR), path))self._cwd = b(self.normalize(path))def getcwd(self):"""Return the "current working directory" for this SFTP session, asemulated by Paramiko. If no directory has been set with `chdir`,this method will return ``None``... versionadded:: 1.4"""# TODO: make class initialize with self._cwd set to self.normalize('.')return self._cwd and u(self._cwd)def _transfer_with_callback(self, reader, writer, file_size, callback):size = 0while True:data = reader.read(32768)writer.write(data)size += len(data)if len(data) == 0:breakif callback is not None:callback(size, file_size)return sizedef putfo(self, fl, remotepath, file_size=0, callback=None, confirm=True):"""Copy the contents of an open file object (``fl``) to the SFTP server as``remotepath``. Any exception raised by operations will be passedthrough.The SFTP operations use pipelining for speed.:param fl: opened file or file-like object to copy:param str remotepath: the destination path on the SFTP server:param int file_size:optional size parameter passed to callback. If none is specified,size defaults to 0:param callable callback:optional callback function (form: ``func(int, int)``) that acceptsthe bytes transferred so far and the total bytes to be transferred(since 1.7.4):param bool confirm:whether to do a stat() on the file afterwards to confirm the filesize (since 1.7.7):return:an `.SFTPAttributes` object containing attributes about the givenfile... versionadded:: 1.10"""with self.file(remotepath, 'wb') as fr:fr.set_pipelined(True)size = self._transfer_with_callback(reader=fl, writer=fr, file_size=file_size, callback=callback)if confirm:s = self.stat(remotepath)if s.st_size != size:raise IOError('size mismatch in put! %d != %d' % (s.st_size, size))else:s = SFTPAttributes()return sdef put(self, localpath, remotepath, callback=None, confirm=True):"""Copy a local file (``localpath``) to the SFTP server as ``remotepath``.Any exception raised by operations will be passed through. Thismethod is primarily provided as a convenience.The SFTP operations use pipelining for speed.:param str localpath: the local file to copy:param str remotepath: the destination path on the SFTP server. Notethat the filename should be included. Only specifying a directorymay result in an error.:param callable callback:optional callback function (form: ``func(int, int)``) that acceptsthe bytes transferred so far and the total bytes to be transferred:param bool confirm:whether to do a stat() on the file afterwards to confirm the filesize:return: an `.SFTPAttributes` object containing attributes about the given file.. versionadded:: 1.4.. versionchanged:: 1.7.4``callback`` and rich attribute return value added... versionchanged:: 1.7.7``confirm`` param added."""file_size = os.stat(localpath).st_sizewith open(localpath, 'rb') as fl:return self.putfo(fl, remotepath, file_size, callback, confirm)def getfo(self, remotepath, fl, callback=None):"""Copy a remote file (``remotepath``) from the SFTP server and write toan open file or file-like object, ``fl``. Any exception raised byoperations will be passed through. This method is primarily providedas a convenience.:param object remotepath: opened file or file-like object to copy to:param str fl:the destination path on the local host or open file object:param callable callback:optional callback function (form: ``func(int, int)``) that acceptsthe bytes transferred so far and the total bytes to be transferred:return: the `number <int>` of bytes written to the opened file object.. versionadded:: 1.10"""file_size = self.stat(remotepath).st_sizewith self.open(remotepath, 'rb') as fr:fr.prefetch(file_size)return self._transfer_with_callback(reader=fr, writer=fl, file_size=file_size, callback=callback)return sizedef get(self, remotepath, localpath, callback=None):"""Copy a remote file (``remotepath``) from the SFTP server to the localhost as ``localpath``. Any exception raised by operations will bepassed through. This method is primarily provided as a convenience.:param str remotepath: the remote file to copy:param str localpath: the destination path on the local host:param callable callback:optional callback function (form: ``func(int, int)``) that acceptsthe bytes transferred so far and the total bytes to be transferred.. versionadded:: 1.4.. versionchanged:: 1.7.4Added the ``callback`` param"""with open(localpath, 'wb') as fl:size = self.getfo(remotepath, fl, callback)s = os.stat(localpath)if s.st_size != size:raise IOError('size mismatch in get! %d != %d' % (s.st_size, size))### internals...def _request(self, t, *arg):num = self._async_request(type(None), t, *arg)return self._read_response(num)def _async_request(self, fileobj, t, *arg):# this method may be called from other threads (prefetch) self._lock.acquire()try:msg = Message()msg.add_int(self.request_number)for item in arg:if isinstance(item, long):msg.add_int64(item)elif isinstance(item, int):msg.add_int(item)elif isinstance(item, (string_types, bytes_types)):msg.add_string(item)elif isinstance(item, SFTPAttributes):item._pack(msg)else:raise Exception('unknown type for %r type %r' % (item, type(item)))num = self.request_numberself._expecting[num] = fileobjself.request_number += 1finally:self._lock.release()self._send_packet(t, msg)return numdef _read_response(self, waitfor=None):while True:try:t, data = self._read_packet()except EOFError as e:raise SSHException('Server connection dropped: %s' % str(e))msg = Message(data)num = msg.get_int()self._lock.acquire()try:if num not in self._expecting:# might be response for a file that was closed before responses came backself._log(DEBUG, 'Unexpected response #%d' % (num,))if waitfor is None:# just doing a single checkbreakcontinuefileobj = self._expecting[num]del self._expecting[num]finally:self._lock.release()if num == waitfor:# synchronousif t == CMD_STATUS:self._convert_status(msg)return t, msgif fileobj is not type(None):fileobj._async_response(t, msg, num)if waitfor is None:# just doing a single checkbreakreturn None, Nonedef _finish_responses(self, fileobj):while fileobj in self._expecting.values():self._read_response()fileobj._check_exception()def _convert_status(self, msg):"""Raises EOFError or IOError on error status; otherwise does nothing."""code = msg.get_int()text = msg.get_text()if code == SFTP_OK:returnelif code == SFTP_EOF:raise EOFError(text)elif code == SFTP_NO_SUCH_FILE:# clever idea from john a. meinel: map the error codes to errnoraise IOError(errno.ENOENT, text)elif code == SFTP_PERMISSION_DENIED:raise IOError(errno.EACCES, text)else:raise IOError(text)def _adjust_cwd(self, path):"""Return an adjusted path if we're emulating a "current workingdirectory" for the server."""path = b(path)if self._cwd is None:return pathif len(path) and path[0:1] == b_slash:# absolute pathreturn pathif self._cwd == b_slash:return self._cwd + pathreturn self._cwd + b_slash + path class?SFTPClient( )源碼
    • 基于用戶名密碼上傳下載
    • 基于公鑰密鑰上傳下載
  • 安裝 paramiko?

    pip3 install paramiko

    操作 paramiko?

    SSHClient

  • SSHClient -?用于連接遠程服務器并執(zhí)行基本命令?
    import paramikossh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname='172.16.201.134', port=22, username='janice', password='janice123') stdin, stdout, stderr = ssh.exec_command('ls -la')results = stdout.read() print(results.decode()) ssh.close() paramiko.SSHClient( )密碼登入 import paramikoprivate_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname='172.16.201.134', port=22, username='user', pkey=private_key)stdin, stdout, stderr = ssh.exec_command('ls -la')results = stdout.read() print(results.decode()) ssh.close() paramiko.SSHClient( )公鑰密鑰連接
  • 創(chuàng)建 Transport 對象來連接
    import paramikoip_port = ('172.16.201.134',22,) transport = paramiko.Transport(ip_port) transport.connect(username='user',password='user')ssh = paramiko.SSHClient() ssh._transport = transportstdin, stdout, stderr = ssh.exec_command('df') results = stdout.read() # 獲取命令結果print(results.decode()) transport.close() paramiko.Transport(ip_port)密碼登入 import paramikoprivate_key = paramiko.RSAKey.from_private_key_file('/Users/jcchoiling/.ssh/vm.key')ip_port = ('172.16.201.134',22,) transport = paramiko.Transport(ip_port) transport.connect(username='user',pkey=private_key)ssh = paramiko.SSHClient() ssh._transport = transportstdin, stdout, stderr = ssh.exec_command('df') results = stdout.read() # 獲取命令結果print(results.decode()) transport.close() paramiko.Transport(ip_port)公鑰密鑰連接

    ?

  • SFPTClient

  • SFTPClient -?用于連接遠程服務器并執(zhí)行上傳下載
    第一步:創(chuàng)建 transport 通道 e.g.?paramiko.Transport(ip_port)?連接對象,負責上傳文件到目標服務器端
    第二步:transport 需要連接上服務器端,輸入用戶名和密碼
    第三步:創(chuàng)建 SFTPClient 對象然后把 transport作為參數(shù)傳入 e.g.?paramiko.SFTPClient.from_transport(transport)
    第四步:可以調用 sftp.put/ sftp.get 方法來上傳和下載文件
    ? ? ? ??四、一)sftp.put(localfile, remotefile) 從本地上傳到遠端
    ? ? ? ??四、二)sftp.get(remotefile, localfile) 從遠端下載到本地
    第五步:關閉 transport 通道
    import paramiko ip_port = ('172.16.201.134',22,) transport = paramiko.Transport(ip_port) transport.connect(username='user',password='user')sftp = paramiko.SFTPClient.from_transport(transport) sftp.put('/Users/jcchoiling/Desktop/movies.dat','/home/user/m1.dat') sftp.get('/home/user/start.sh','/Users/jcchoiling/Desktop/start.sh')transport.close() paramiko.SFTPClient.from_transport(transport)密碼登入 import paramikoprivate_key = paramiko.RSAKey.from_private_key_file('/Users/jcchoiling/.ssh/vm.key')ip_port = ('172.16.201.134',22,) transport = paramiko.Transport(ip_port) transport.connect(username='user',pkey=private_key)sftp = paramiko.SFTPClient.from_transport(transport) sftp.put('/Users/jcchoiling/Desktop/movies.dat','/home/user/m1.dat') sftp.get('/home/user/start.sh','/Users/jcchoiling/Desktop/start.sh')transport.close() paramiko.SFTPClient.from_transport(transport)公鑰密鑰連接
  • ?

    上下文操作應用

    ?

    ?

    ?

    ?

    ?

    初探堡壘機

    ?

    ?

    ?

    ?

    ?

    本周作業(yè)

    作業(yè):開發(fā)一個由數(shù)據(jù)庫管理的主機管理系統(tǒng),主機分組、分用戶權限管理

  • 所有的用戶操作日志要保留在數(shù)據(jù)庫中
  • 每個用戶登錄堡壘機后,只需要選擇具體要訪問的設置,就連接上了,不需要再輸入目標機器的訪問密碼
  • 允許用戶對不同的目標設備有不同的訪問權限,例:
  • 對10.0.2.34 有mysql 用戶的權限
  • 對192.168.3.22 有root用戶的權限
  • 對172.33.24.55 沒任何權限
  • 分組管理,即可以對設置進行分組,允許用戶訪問某組機器,但對組里的不同機器依然有不同的訪問權限?
  • 考核題

    試說明你寫這個作業(yè)的思路:

  • 首先看到管理主機組,這表明可能會有多于一臺服務器,然后假設我是管理100臺 Hadoop 服務器的管理員。
  • 然后設計數(shù)據(jù)庫的表結構:
    • 有本地用戶 User
    • 管理組表 Groups
    • 本地用戶和組的關系表 User-Groups
    • 服務器表 Hosts
    • 遠程用戶表 RemoteUsers
    • 管理組表、遠程用戶和服務器的關系表 Hosts-Groups-RemoteUsers
    • 記錄表 Audit Log
  • 然后可以從功能方面思考:
    • 查看用戶信息
    • 創(chuàng)建群組
    • 創(chuàng)建用戶
    • 創(chuàng)建服務器
    • 創(chuàng)建遠程用戶
    • 刪除群組
    • 刪除用戶
    • 刪除服務器
    • 新增用戶到指定群組
    • 新增服務器到指定群組
    • 初始化數(shù)據(jù)庫
    • 刪除數(shù)據(jù)庫
    • 遠程連接 
  • 最后是把功能的流程關連在一起,比如說,你會想像當管理員一打開程序,第一步是什么、下一步又會是什么,這樣的思路去設計你程序的流程圖。
    • 用戶登入 (登入需要認證,可以添加登入3次鎖定帳號)
    • 登入后看到功能列表選單
    • 每一個功能用一個函數(shù)來表達
    • 當程序遇上非如期的輸入時的處理方法 (Error Handling)
    • 優(yōu)雅地退出程序 (Exit program)

    [知識點:重點是如果用 Python 操作數(shù)據(jù)庫,從數(shù)據(jù)庫中讀寫數(shù)據(jù)。]

    數(shù)據(jù)庫表結構:

    ?

    程序運行結果:

    ?

    總結

    第五階段主要是學習了如何用 Python 來實現(xiàn)網絡編程,第一部份是介紹了基本的網絡協(xié)議,其中重點是 TCP/IP 協(xié)議,學習如何寫服務器端和客戶端的 socket,還數(shù)據(jù)可以通個 TCP/IP 來輸送和接收數(shù)據(jù);第二部份介紹了 Python中的線程、進程和協(xié)程。然后還學了線程鎖與進程鎖;學了自定義線程池。第三部份學了消息隊列的概念,分別是 Python 內置的 Queue 功能和 RabbitMQ的功能。具體介紹了 RabbitMQ 的發(fā)布和訂閱、主題模式和 RPC 通信。第四部份學了數(shù)據(jù)庫的操作:分別是 MySQL 中的原生 SQL話句和如何用 Python 的 pymysql 來操作 MySQL 數(shù)據(jù)庫。在了這個基礎之后,便深入介紹 Python 中的 ORM,當中最具代表性的就是 SQLAlchemy?模塊,學習如何用 Python 調用?SQLAlchemy 中的功能來操作 MySQL數(shù)據(jù)庫,這是一個很有意思的單元。?

    ?

    ?

    ?

    參考資料?

    銀角大王:1)?MySQL 操作

    金角大王:1)?Python之路,Day10~11 - 那就做個堡壘機吧

         2)?python 之路,Day11 - sqlalchemy ORM

         ? 3)?金角大王教你如何做個堡壘機

    OReilly.Essential.SQLAlchemy

    ?

    轉載于:https://www.cnblogs.com/jcchoiling/p/5991551.html

    總結

    以上是生活随笔為你收集整理的第十三章:Python の 网络编程进阶(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。