找回密码
 注册
查看: 1893|回复: 0

openstack nova server.py文件内容

[复制链接]

1

主题

0

回帖

12

积分

管理员

积分
12
QQ
发表于 2021-9-2 17:47:14 | 显示全部楼层 |阅读模式
# Licensed under the Apache License, Version 2.0 (the "License"); you may: s5 c5 Q. C" R: ]! W
# not use this file except in compliance with the License. You may obtain6 O- ]$ ?, `" @8 C5 J
# a copy of the License at5 d2 F6 |# w: u2 `6 h
#
5 a1 {6 X; a3 u1 e* A$ y8 n" Z#      http://www.apache.org/licenses/LICENSE-2.02 t! |4 c& z) a' [  x+ n
#
8 t  U. k* G% p# _' X2 a# Unless required by applicable law or agreed to in writing, software. {" s, d7 `; ^9 Q9 E  s  l
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0 H4 Y# T/ |; ~% T$ i9 R# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1 A! {3 r% F9 M# License for the specific language governing permissions and limitations7 G$ G( f4 r' f/ ]* ~2 s- _3 T
# under the License.' W* W0 H0 |0 q; |% D& E* E* K
from openstack.compute.v2 import metadata
* D9 z5 x- K- t' f" \from openstack import exceptions
1 d( h6 a1 C) l3 J* a$ Tfrom openstack.image.v2 import image
* _/ i, J" _7 l7 s& e* gfrom openstack import resource+ s/ H2 i; S' O) `
from openstack import utils
9 Q: I& t% ^  d2 U, I5 u& i  H; d$ w- R& S) U) b7 z& d
CONSOLE_TYPE_ACTION_MAPPING = {
. M7 V5 X* g4 F    'novnc': 'os-getVNCConsole',
& h) ?! [7 }9 e4 m  B) c4 Z    'xvpvnc': 'os-getVNCConsole',( f; N* s% W0 W7 _. _
    'spice-html5': 'os-getSPICEConsole'," Z0 {- V2 g/ g6 g) D* y) D2 N; l
    'rdp-html5': 'os-getRDPConsole',
4 \# v. B1 s3 X' M- i9 H( K    'serial': 'os-getSerialConsole'5 E+ }5 \4 w1 N: ]
}
/ P/ L# R. j! f  K: D% @2 Q# P3 M" A, d- v4 S
class Server(resource.Resource, metadata.MetadataMixin, resource.TagMixin):
& B/ j0 U* w. L! l4 }& }3 O( L    resource_key = 'server'
1 O7 u6 m) F* f. v* O2 n& z6 c    resources_key = 'servers'6 ~: F1 Y8 r3 u5 m
    base_path = '/servers'1 F; w8 \4 w4 r: Q/ Z/ _, j
    # capabilities
! ~1 W/ d6 u6 J- B' q: _6 q% L7 F9 E    allow_create = True
' u# B8 K) X" v0 G7 C    allow_fetch = True
; ?6 a# w5 o* d6 s/ V, N$ q    allow_commit = True" x1 v+ p. W7 d/ f" t- x( ?
    allow_delete = True1 o+ L, ]  a. Z) D' \( ^0 x6 f! C
    allow_list = True
* ~/ y" A2 u6 P3 j# D. n& m! @    _query_mapping = resource.QueryParameters(
5 z/ a' y5 d) X7 @) a9 h        "auto_disk_config", "availability_zone",& s# S$ z9 Y9 [+ N
        "created_at", "description", "flavor",$ c9 s7 S+ T* \" y" x
        "hostname", "image", "kernel_id", "key_name",/ h( b1 e4 g$ z3 j5 b
        "launch_index", "launched_at", "locked_by", "name",. i& y" x2 T! M0 U. o7 y/ l1 Q# j' o
        "node", "power_state", "progress", "project_id", "ramdisk_id",# i7 {5 Y6 G) Y9 n
        "reservation_id", "root_device_name",
7 d5 s9 \" ~9 C6 G) m: x        "status", "task_state", "terminated_at", "user_id",
8 u$ Q3 ]# y# z$ P& U6 W) y; i        "vm_state",  Z7 d. s1 I) X* J, p3 Y
        "sort_key", "sort_dir",3 F; q. x, T/ \- j4 r) W
        access_ipv4="access_ip_v4",0 A/ j4 B0 y: ^! [- _
        access_ipv6="access_ip_v6",
, C$ R7 M/ c$ r1 u. g        has_config_drive="config_drive",  [9 V! a. M% |, ]
        deleted_only="deleted",
1 W1 w0 s: H" w  j3 m. ^        compute_host="host",4 Q1 S1 V) r- Z- a# m- T
        is_soft_deleted="soft_deleted",. Z' B& C6 p, d4 s
        ipv4_address="ip",! _$ c$ H* l5 g) g1 ~0 ^9 `2 W0 B
        ipv6_address="ip6",
5 \' q! N4 {3 W        changes_since="changes-since",# m! |) O5 r2 B. c* Q, o
        changes_before="changes-before",3 E( S' s% Q1 ?: X
        id="uuid",/ A' Y7 c) O8 p2 n# X2 N
        all_projects="all_tenants"," L: b9 s+ o' I) J. p) I
        **resource.TagMixin._tag_query_parameters
: w! A& K' ?* D; l- [6 D    )
1 W& e# z3 Z0 {5 W. n    _max_microversion = '2.72'; h( n/ d4 L8 u
    #: A list of dictionaries holding links relevant to this server.
# y- X( w' ^- M: }/ {# L    links = resource.Body('links')
- B. S/ i' e( v( |* m" F; O    access_ipv4 = resource.Body('accessIPv4')
  R( L6 ?# ^+ p% M: T    access_ipv6 = resource.Body('accessIPv6')) W1 `9 w0 ]5 `
    #: A dictionary of addresses this server can be accessed through.: X) `) A! O2 b) l9 u
    #: The dictionary contains keys such as ``private`` and ``public``,
0 G3 e" E1 S) r# @; I; O* f! P+ r    #: each containing a list of dictionaries for addresses of that type.: q+ Z5 h& b: R3 H6 E5 C
    #: The addresses are contained in a dictionary with keys ``addr``
! X) N9 @/ K6 f. e    #: and ``version``, which is either 4 or 6 depending on the protocol
2 p) }: ?9 K- K' t    #: of the IP address. *Type: dict*
' j" k* c  l0 Z+ n, G    addresses = resource.Body('addresses', type=dict)
- O* z7 C: b' ^4 t/ x    #: When a server is first created, it provides the administrator password.) g4 V' }5 L, `: r
    admin_password = resource.Body('adminPass')/ E  ~' U% S/ {9 U, X) K2 R
    #: A list of an attached volumes. Each item in the list contains at least- d' c* m" _/ ^$ m9 R4 M3 O" f" ?( c
    #: an "id" key to identify the specific volumes.
, |( F/ ~0 n& ^. ?+ b    attached_volumes = resource.Body(+ ~* y& x' ]4 W8 E0 g; j; o! G
        'os-extended-volumes:volumes_attached')) f1 u6 C' {5 Q1 G8 S: d8 E2 S
    #: The name of the availability zone this server is a part of.
  I9 r4 U+ T" {1 ?9 {    availability_zone = resource.Body('OS-EXT-AZ:availability_zone')
( y3 n# q1 Z# `& [. o    #: Enables fine grained control of the block device mapping for an* Q5 M4 L  s6 N$ J
    #: instance. This is typically used for booting servers from volumes.
3 r1 B# {4 Z8 Z1 x5 t: O& P, W/ R    block_device_mapping = resource.Body('block_device_mapping_v2'). ^7 d2 r% O/ t- n9 K  ], e( S$ a
    #: Indicates whether or not a config drive was used for this server.: @9 S1 A6 r7 R" N% J. [
    config_drive = resource.Body('config_drive')8 b# R: p/ g2 g* T- c% ]  n) p( d6 d
    #: The name of the compute host on which this instance is running.3 G8 _4 f  v: t, E6 N2 k" M) M7 K
    #: Appears in the response for administrative users only.
" h: h1 w1 v% ^. s    compute_host = resource.Body('OS-EXT-SRV-ATTR:host'), ?+ b# a% B9 r1 U
    #: Timestamp of when the server was created.  N6 @' N* w! Z3 l5 m' x* \2 z* h" `
    created_at = resource.Body('created')" H& m- X9 G" d! H6 V1 N
    #: The description of the server. Before microversion7 D+ g  Z0 n- ~( c0 c: x! g$ y  L
    #: 2.19 this was set to the server name.
  i* X- ?# s- [* P. w    description = resource.Body('description')
) Z1 ]) D, s0 Q' Z    #: The disk configuration. Either AUTO or MANUAL.
  h" ^" V# d8 c' _% e4 X    disk_config = resource.Body('OS-DCF:diskConfig')
2 {  \) U! h6 y; L9 L+ s& \: ?, ^    #: The flavor reference, as a ID or full URL, for the flavor to use for0 R' k3 C* l2 d# r3 u# J
    #: this server.
7 t7 j* x5 k- n* Y    flavor_id = resource.Body('flavorRef')
9 j) p; X& u. c5 B. F2 G    #: The flavor property as returned from server.
8 U/ X! T% ]7 \# g    # TODO(gtema): replace with flavor.Flavor addressing flavor.original_name
+ r& Q9 s6 H+ k! \& }1 J: Y& l    flavor = resource.Body('flavor', type=dict)
" l- M* e/ i) q3 @    #: Indicates whether a configuration drive enables metadata injection.
6 L" _# M' k7 m: l    #: Not all cloud providers enable this feature.. N  Q8 x& y# \
    has_config_drive = resource.Body('config_drive')
4 |0 m* b) a! N# I$ d    #: An ID representing the host of this server.
5 ^, K  l, s, k  z    host_id = resource.Body('hostId')4 A: X5 ]) d8 {' h) S
    #: The host status." e+ r6 p  D1 \, d1 V+ O3 j( n
    host_status = resource.Body('host_status'): E" m1 I' N6 Y; j
    #: The hostname set on the instance when it is booted.
: T  m% T6 I4 g. A4 M% |$ Y    #: By default, it appears in the response for administrative users only.5 {3 X9 v  o# \9 q0 O
    hostname = resource.Body('OS-EXT-SRV-ATTR:hostname')9 o; i; B1 h. v7 o5 z6 _
    #: The hypervisor host name. Appears in the response for administrative3 M9 }0 }3 S7 T: z$ W9 _& l& Y7 b; j
    #: users only.
* {0 S- J5 b' s& ~6 v    hypervisor_hostname = resource.Body('OS-EXT-SRV-ATTR:hypervisor_hostname')4 P9 j+ n# h$ T! T  _' Y  K
    #: The image reference, as a ID or full URL, for the image to use for
+ g* X: R4 s6 P+ O$ h& z4 E& t& |    #: this server.& t! }  G2 Z, j7 @: z
    image_id = resource.Body('imageRef')" k9 E. S% u, q+ c! m
    #: The image property as returned from server.7 T5 l& t) r0 }) |- ]6 E' V" M- |0 v
    image = resource.Body('image', type=image.Image). @) {0 N& Y/ E, u5 J8 Z
    #: The instance name. The Compute API generates the instance name from the
$ E  G5 ~2 l0 p, ^4 h* q* Y    #: instance name template. Appears in the response for administrative users
( @7 w5 s1 B: ^    #: only.4 w! e2 p  e/ U5 U! V4 q7 S
    instance_name = resource.Body('OS-EXT-SRV-ATTR:instance_name'); ]% o7 g: ?# z8 A7 R* C! @
    # The locked status of the server6 ]. h* q2 E/ a5 O& `) E; i; r
    is_locked = resource.Body('locked', type=bool)+ ~) w' H1 O' F6 Y
    #: The UUID of the kernel image when using an AMI. Will be null if not.7 W: Y# }$ K9 z  f
    #: By default, it appears in the response for administrative users only.7 x+ o% h1 `1 i' ], `8 p3 [" M: |
    kernel_id = resource.Body('OS-EXT-SRV-ATTR:kernel_id')
* m6 a$ e6 K* \    #: The name of an associated keypair. a) I! @$ V' j( T
    key_name = resource.Body('key_name')
+ m2 ~( s6 g4 h! A& I    #: When servers are launched via multiple create, this is the
4 d4 ?3 _  g6 H7 D7 M    #: sequence in which the servers were launched. By default, it
( \- e; i; W) L  N    #: appears in the response for administrative users only.
0 [; o7 k$ [+ E; R$ |    launch_index = resource.Body('OS-EXT-SRV-ATTR:launch_index', type=int)
! [) \/ C" Q- u    #: The timestamp when the server was launched.2 l% R6 \' X4 D* O) T/ b
    launched_at = resource.Body('OS-SRV-USG:launched_at')
% t) J% \; @+ B9 n* j    #: The maximum number of servers to create.
' L  r0 `$ @% G# e    max_count = resource.Body('max_count')8 J" ^& F' |3 F) w4 k# Y
    #: Metadata stored for this server. *Type: dict*6 `0 k) S) q4 J  u9 i( M5 }1 @) _
    metadata = resource.Body('metadata', type=dict)
% o" e* ?  _/ a  H" L$ [    #: The minimum number of servers to create.
0 Y9 w, L$ Q2 b    min_count = resource.Body('min_count')8 m3 J: I& m% D: i' T% B, P
    #: A networks object. Required parameter when there are multiple0 B+ F% I; q. c) m; J
    #: networks defined for the tenant. When you do not specify the
9 ]4 E" g. G# P+ e  R* Z; t# X    #: networks parameter, the server attaches to the only network
) k1 O  U9 k9 t- _8 n& Z- `8 X2 {    #: created for the current tenant.# i" `' l6 M: M
    networks = resource.Body('networks')
2 J. g! R5 l9 B: N    #: The power state of this server.* A0 P. H6 P  m
    power_state = resource.Body('OS-EXT-STS:power_state')2 O9 _5 A* A( t1 q1 K7 g
    #: While the server is building, this value represents the percentage
/ p6 U. k6 ]6 M2 y! g: _/ j" |/ s7 Q    #: of completion. Once it is completed, it will be 100.  *Type: int*7 x, ^6 j# e5 r. u6 D3 B+ _5 r! Z
    progress = resource.Body('progress', type=int)
7 A+ ]; w' s. b5 i1 K" ^    #: The ID of the project this server is associated with.4 d+ G7 _" x9 Y, ~( I0 o
    project_id = resource.Body('tenant_id')4 w* b2 g9 p; y% O5 k: Y* ?$ ]
    #: The UUID of the ramdisk image when using an AMI. Will be null if not.
/ [. U* {1 ^2 d& |( ^    #: By default, it appears in the response for administrative users only.( _( e; e- E. j: q
    ramdisk_id = resource.Body('OS-EXT-SRV-ATTR:ramdisk_id')# `& M, C9 z# Y  ?! |
    #: The reservation id for the server. This is an id that can be
; ~( |9 }9 N; f    #: useful in tracking groups of servers created with multiple create,5 x, K, M! Y& I$ r6 {. f; e
    #: that will all have the same reservation_id. By default, it appears: o/ ]  o/ B6 j) o6 g3 x( T; z7 H9 w
    #: in the response for administrative users only.; r+ C8 a0 @9 G
    reservation_id = resource.Body('OS-EXT-SRV-ATTR:reservation_id')
2 k5 z* j! I/ w/ E, y& v    #: The root device name for the instance By default, it appears in the+ y0 L9 D1 @. |, F9 C0 L" h* ?
    #: response for administrative users only.) v7 w0 _, \4 I4 t
    root_device_name = resource.Body('OS-EXT-SRV-ATTR:root_device_name')7 m- l. a  P# U3 t7 U, O- d
    #: The dictionary of data to send to the scheduler.3 L5 L2 [& b: l5 M* z
    scheduler_hints = resource.Body('OS-SCH-HNT:scheduler_hints', type=dict)
( d  Q$ l* N8 V3 i  p2 T$ S- ]    #: A list of applicable security groups. Each group contains keys for
: H- Q5 i1 n# r9 h    #: description, name, id, and rules.5 g: n$ l+ Y0 b- |# u" y" [
    security_groups = resource.Body('security_groups',- {% h+ @4 n! w0 ^0 D
                                    type=list, list_type=dict)# B. P+ I. L5 g1 H) N
    #: The UUIDs of the server groups to which the server belongs.0 ?7 C$ i  E4 M' Y% r! z- x
    #: Currently this can contain at most one entry.8 K. L* g: y) y/ N# e: R9 o
    server_groups = resource.Body('server_groups', type=list)  v# F$ R7 A  F6 Z8 v
    #: The state this server is in. Valid values include ``ACTIVE``,
. b6 V1 v1 e5 T( X+ z    #: ``BUILDING``, ``DELETED``, ``ERROR``, ``HARD_REBOOT``, ``PASSWORD``,
  O* Z: Z0 W$ @$ {; `6 v    #: ``PAUSED``, ``REBOOT``, ``REBUILD``, ``RESCUED``, ``RESIZED``,( r9 F4 J5 W" `1 J3 U
    #: ``REVERT_RESIZE``, ``SHUTOFF``, ``SOFT_DELETED``, ``STOPPED``,
7 ^0 h+ V) ^8 G4 U0 z3 ]6 Z    #: ``SUSPENDED``, ``UNKNOWN``, or ``VERIFY_RESIZE``.
! L) ], U$ ~. V2 ?7 X0 H    status = resource.Body('status')( ]3 G" P% c. m1 ^& e
    #: The task state of this server.
* `( g& ]% A$ d3 q$ `2 f    task_state = resource.Body('OS-EXT-STS:task_state')* ^. ~: e( Y( c2 i+ |
    #: The timestamp when the server was terminated (if it has been).
2 E1 X: |# {; t" H, S& K' l    terminated_at = resource.Body('OS-SRV-USG:terminated_at')
8 K0 u! I4 N" q% N0 @    #: A list of trusted certificate IDs, that were used during image
  D+ a0 ~+ P1 w6 V7 ~. h0 K    #: signature verification to verify the signing certificate.
: x# t7 |- j- \* X8 n& D  \    trusted_image_certificates = resource.Body(4 o3 a9 Q/ ^/ q
        'trusted_image_certificates', type=list)# J0 X  v! d9 ?/ \- r
    #: Timestamp of when this server was last updated.
, |9 _( d$ q3 d3 H" E    updated_at = resource.Body('updated')
4 X& @; v! l2 F% B8 w. O, q# g    #: Configuration information or scripts to use upon launch.
  x* ^2 u! `7 x# g  F7 N    #: Must be Base64 encoded.( y' D- r# H# g# @" u! w9 d) j
    user_data = resource.Body('OS-EXT-SRV-ATTR:user_data')1 X: g" x6 i; a- A+ q* Z# u4 D- K* v
    #: The ID of the owners of this server.' F9 z. Q3 Z' t1 Z; ?) d
    user_id = resource.Body('user_id')' \8 R' G  r; F7 a5 O, y# q
    #: The VM state of this server.) j- ^0 {" Z+ p
    vm_state = resource.Body('OS-EXT-STS:vm_state')% q% v4 b& }, y4 }
    def _prepare_request(self, requires_id=True, prepend_key=True,
$ [! q5 Z( L$ r$ {# a                         base_path=None, **kwargs):
& u8 w- A. m( l! w        request = super(Server, self)._prepare_request(requires_id=requires_id,6 l5 [" ^$ s& r: R; i7 o
                                                       prepend_key=prepend_key,  I0 Q9 M( {7 J0 q
                                                       base_path=base_path)
2 Q5 r' c5 g2 P$ ~$ r2 W        server_body = request.body[self.resource_key]! \% ~' U' B5 f! j
        # Some names exist without prefix on requests but with a prefix
8 b$ r5 h+ e1 o, T" w4 N        # on responses. If we find that we've populated one of these# S. \& v5 x' |* M3 E7 ?- L
        # attributes with something and then go to make a request, swap out
' W! x% T' X- r' K        # the name to the bare version.
$ v; i  ?, P% ^, R: I. x        # Availability Zones exist with a prefix on response, but not request, H# [! k6 g, s0 Q( S  e
        az_key = "OS-EXT-AZ:availability_zone"& R8 t4 z1 x! ^
        if az_key in server_body:
9 ]2 f/ v3 O# g* v& h7 q            server_body["availability_zone"] = server_body.pop(az_key)! z5 f" T( P) K. i9 r, P4 e
        # User Data exists with a prefix on response, but not request
. J1 y6 B$ ?& E4 [0 R. j; _        ud_key = "OS-EXT-SRV-ATTR:user_data"
8 q2 ?; X5 K+ g        if ud_key in server_body:; K8 g8 Q/ t1 i9 ]1 d
            server_body["user_data"] = server_body.pop(ud_key)
6 Q' Y. x0 W4 J& ^0 z        # Scheduler hints are sent in a top-level scope, not within the3 U5 i: z! S9 e5 T3 }+ H7 R( U2 s
        # resource_key scope like everything else. If we try to send
! ~/ ]6 e! I  p/ M. Z. y; C" T        # scheduler_hints, pop them out of the resource_key scope and into
8 O! W' x7 S( t, O, m6 i( q        # their own top-level scope.
' V8 A, r( M% |3 ?8 n        hint_key = "OS-SCH-HNT:scheduler_hints"
$ K: O* T/ z3 n/ `: M/ b2 f. n2 w        if hint_key in server_body:' b/ c# C) {, [
            request.body[hint_key] = server_body.pop(hint_key)
) W. U. z: [# X$ |2 c1 Y        return request
  ]' _7 \( Z' Y! ~; Y' F2 s    def _action(self, session, body, microversion=None):* N: E' d1 z6 s  i/ b6 u4 p  k
        """Preform server actions given the message body."""
/ @/ W7 X" Z! K& L6 W% J        # NOTE: This is using Server.base_path instead of self.base_path7 O* H+ }  c0 Q$ j- f
        # as both Server and ServerDetail instances can be acted on, but
; C  v; N& }8 N5 ~8 b1 S) P8 j        # the URL used is sans any additional /detail/ part.
, i5 a  |7 w8 E2 S( _  I; g( N) Y" {; s        url = utils.urljoin(Server.base_path, self.id, 'action')
2 G6 N) V* m% v  C9 w        headers = {'Accept': ''}
& E4 O/ v! H/ ]) }: C/ E        response = session.post(, ?8 |5 R& j, l. d/ q
            url, json=body, headers=headers, microversion=microversion)
( {: N1 f" X6 f: v        exceptions.raise_from_response(response)8 c, w+ {. h: k6 z
        return response9 L8 p, _* z8 i0 {
    def change_password(self, session, new_password):
" _  u  G. J' ^6 `& M! c        """Change the administrator password to the given password."""
. }0 c3 Y% c  j% L1 [        body = {'changePassword': {'adminPass': new_password}}, J8 W5 @0 s/ P$ G/ {
        self._action(session, body)5 g5 c( s4 B  [9 b
    def get_password(self, session):1 `% f' x. Q& D/ ~  v+ ]9 [
        """Get the encrypted administrator password."""3 T0 N# c2 g! }% G3 X( _( [8 A% H
        url = utils.urljoin(Server.base_path, self.id, 'os-server-password')3 c: v( t: w+ z. W1 _+ P
        response = session.get(url)
% `: ^  Y7 ]) T, T- a. R# o4 x! L: N        exceptions.raise_from_response(response)
3 y$ Z( G' t2 ]8 \  M! P        data = response.json(). ~. D6 T) x% G5 ?4 s, R( W
        return data.get('password')# p2 A, g- T0 `) Z' p
    def reboot(self, session, reboot_type):1 [: s) z! r5 h4 s  L" Q" E
        """Reboot server where reboot_type might be 'SOFT' or 'HARD'."""
; b% b2 R, s% R' @0 L; A        body = {'reboot': {'type': reboot_type}}
. A5 c% o7 M" y& y( z$ k9 p        self._action(session, body): ?0 j9 s9 h7 C6 H; v5 p
    def force_delete(self, session):
0 K! d, ]2 [6 E9 b0 |' _3 j        """Force delete a server."""
3 A( Q. B/ l5 d$ Q0 p        body = {'forceDelete': None}  v# w$ @6 m& e! t2 E
        self._action(session, body)
2 y& u0 ~0 e: u5 W" y: W; o3 l4 b    def rebuild(self, session, name=None, admin_password=None,
) [8 C' ~( Z% N6 O5 I1 C: I0 z                preserve_ephemeral=False, image=None,
9 f; O; y9 s$ ~9 }2 f3 a+ D                access_ipv4=None, access_ipv6=None,# P1 S& S  P3 V1 X9 s6 N
                metadata=None, user_data=None):; a" j# o0 h, ~* }3 G1 N& q
        """Rebuild the server with the given arguments."""" U6 W$ f; \& b. F% \$ g: ]
        action = {
% V7 w  p3 n+ |+ P0 o; R            'preserve_ephemeral': preserve_ephemeral
( i) ~0 v# ?! o4 g7 ~0 i8 u3 _! m        }8 O4 E0 i0 A! W1 N  _
        if image is not None:
+ n, X+ A( b4 V) I9 v            action['imageRef'] = resource.Resource._get_id(image)& M0 x2 P5 r( n( Q7 k3 I! q
        if name is not None:( C6 Z& d! }+ h$ f" o; p# c
            action['name'] = name
1 ^* d4 o. A( L- ~3 o        if admin_password is not None:
0 W4 v) _! C9 n" n            action['adminPass'] = admin_password: P! T0 N: J) T) r' d
        if access_ipv4 is not None:4 Q! t% }% [) b2 E3 X  [8 y
            action['accessIPv4'] = access_ipv4' {$ k/ g% O! l' |
        if access_ipv6 is not None:
/ X5 `( [" j# b            action['accessIPv6'] = access_ipv6
7 W" C& |$ J0 m% T- x" s3 N. L& d        if metadata is not None:
- a" Z# i1 v, H0 Z7 t; x$ m  j$ ]            action['metadata'] = metadata/ Y# W2 L8 J( o6 _- ?; k  F* r
        if user_data is not None:4 n+ \6 u1 |) S8 ~, x' h* H5 u
            action['user_data'] = user_data
2 O, l' G$ |: }0 g' C7 ?: Y/ a+ i) S        body = {'rebuild': action}4 c2 d7 l: m3 ]$ b) d* `
        response = self._action(session, body)
3 ?( g: X: c7 b, v( B. k        self._translate_response(response)
2 ?/ L7 i: @, D1 V1 L' S        return self
+ l7 b* T4 K- \5 X    def resize(self, session, flavor):4 j5 l" l# O# r2 B3 m
        """Resize server to flavor reference."""+ a% ~1 u9 S  p0 `2 W
        body = {'resize': {'flavorRef': flavor}}
& E0 H2 Z2 D& s2 y, ^7 j        self._action(session, body)
9 a3 D1 y( T* [2 n( ]- R    def confirm_resize(self, session):" A3 U2 a- l6 W9 _* B' p% ^
        """Confirm the resize of the server."""7 q( G8 j. q! G
        body = {'confirmResize': None}  I" O+ y& _+ O: m+ S/ C3 G/ H! r
        self._action(session, body)5 ]7 [- @5 O2 k7 s1 C$ d8 F
    def revert_resize(self, session):
, l- ]" B" M! f( }        """Revert the resize of the server."""
, p+ p0 _; j- J1 q0 B        body = {'revertResize': None}4 k9 I2 t6 O7 a7 h8 g# D' y
        self._action(session, body)
& u) ]3 h: M! ~    def create_image(self, session, name, metadata=None):$ p3 n& b& h  d
        """Create image from server."""" V& n+ O: _* e" p1 P4 W
        action = {'name': name}1 g" d* V  k: M" q, D, Q
        if metadata is not None:6 E( @' E3 P( f6 ?+ t% A- K
            action['metadata'] = metadata7 t8 h! ~1 t2 {" Q2 ]7 s9 y( f
        body = {'createImage': action}
; b' H$ Z& k5 S- ^% T# d        # You won't believe it - wait, who am I kidding - of course you will!$ Z/ I) A& k# ^, {% k
        # Nova returns the URL of the image created in the Location! s! E; ?; p8 k) x' I) j) w
        # header of the response. (what?) But, even better, the URL it responds$ q  U( Q) c, O) _0 f" J2 `
        # with has a very good chance of being wrong (it is built from9 k! e# C4 c5 d6 Q9 d: q2 T
        # nova.conf values that point to internal API servers in any cloud. I9 j3 R# Z" ~# `. F7 M2 ]; g
        # large enough to have both public and internal endpoints.
7 ?" m. c  P- g# Q  E. F7 [, e6 [& p        # However, nobody has ever noticed this because novaclient doesn't5 {6 L$ r$ w! w
        # actually use that URL - it extracts the id from the end of& l5 z7 C; i' D* U. T" s9 e
        # the url, then returns the id. This leads us to question:5 d8 H  A. w" L0 j
        #   a) why Nova is going to return a value in a header- @* b+ u) \2 M7 G
        #   b) why it's going to return data that probably broken/ W, e/ @1 O1 e: a- s
        #   c) indeed the very nature of the fabric of reality
: P7 c9 P1 W" U! s0 E        # Although it fills us with existential dread, we have no choice but6 J( V1 F4 w# V* A# X( Y" o
        # to follow suit like a lemming being forced over a cliff by evil
* J; j* j% R) C        # producers from Disney.+ T. J9 ^! @. u% v
        microversion = None! |9 N- j8 Y: T& ]5 C
        if utils.supports_microversion(session, '2.45'):
" f0 z  \8 _& x2 c0 [/ r            microversion = '2.45'6 S9 g8 P2 y  K! _9 q
        response = self._action(session, body, microversion)
, ~& R" t+ c6 U1 H        body = None: f0 s0 N. {2 k% C
        try:- @, C) g. Y3 _7 m% H0 {5 C& S
            # There might be body, might be not
* J1 f9 I  b* S5 A$ a            body = response.json(), n" L  O. R9 `
        except Exception:
' h! V; R* ]2 ]( W            pass, o% @* S$ C/ _6 h" K$ ?' P
        if body and 'image_id' in body:
6 }3 M: t1 O5 W4 t. M3 c# p! W            image_id = body['image_id']
$ b  n/ B  H) H* \( a. ~        else:
" A( ?3 |' z( J            image_id = response.headers['Location'].rsplit('/', 1)[1]
9 N' _! O" @1 N8 E5 `+ g9 O        return image_id4 b+ Z5 E. |; N: L
    def add_security_group(self, session, security_group_name):' N* x6 w- H$ l# `6 ^
        body = {"addSecurityGroup": {"name": security_group_name}}0 k5 q' n% ^4 f: @
        self._action(session, body)% E, t; Z  E9 h* v; p2 w1 e
    def remove_security_group(self, session, security_group_name):( A6 q  h9 d$ P, m
        body = {"removeSecurityGroup": {"name": security_group_name}}% I" q5 F+ I5 z
        self._action(session, body)7 w1 v' @* p" u
    def reset_state(self, session, state):
4 [+ T) V- i# M  T- V# I        body = {"os-resetState": {"state": state}}
+ p' L: d* V& f/ ?) n9 J( C        self._action(session, body)! G3 i& y' u6 v
    def add_fixed_ip(self, session, network_id):
9 A! T4 J" N& d) {9 ?        body = {"addFixedIp": {"networkId": network_id}}3 T% d# k0 d, K& I
        self._action(session, body)
; Z6 U! W- D  p; W! d5 l  V) Y6 ~3 }    def remove_fixed_ip(self, session, address):
  j6 v' m; i& u7 q4 i. f        body = {"removeFixedIp": {"address": address}}/ M/ }+ u) Y0 D' ?3 m
        self._action(session, body)
# [3 s7 U4 N' p( {& S% o    def add_floating_ip(self, session, address, fixed_address=None):# f! f5 H' |! y, [3 j
        body = {"addFloatingIp": {"address": address}}
$ j: O/ d& H! r: R        if fixed_address is not None:" S+ G! c' b0 |2 ]' O$ Z: m
            body['addFloatingIp']['fixed_address'] = fixed_address
9 g5 ?# T' p: A+ c& r* E        self._action(session, body), W; E# I! r, [9 E& R( h
    def remove_floating_ip(self, session, address):& ]; V8 R! X5 `. {( q2 f  y; v3 H
        body = {"removeFloatingIp": {"address": address}}4 h2 D/ t1 `' R
        self._action(session, body)' i. \( V+ w7 ]8 n( k! V
    def backup(self, session, name, backup_type, rotation):
, f  g8 H7 M7 u7 F3 a$ r9 V        body = {% b9 K5 n' n- O+ j* I5 [, m2 c
            "createBackup": {
* Y- m  v8 N+ I) O, P  E  r                "name": name,
" c2 P1 I6 q0 V( T                "backup_type": backup_type," ]$ q2 U, [7 V) W: g
                "rotation": rotation
4 x2 z3 ~% c% L6 M            }# \( w/ D5 Q6 n( W4 e
        }3 U4 F5 }3 _' A0 v
        self._action(session, body)
( Y' `  O; x$ N: v# E% l    def pause(self, session):8 X- ?4 b, ~* P. }0 W
        body = {"pause": None}/ p1 m9 j6 E8 `6 t; j
        self._action(session, body)
. I$ F5 _$ h- O    def unpause(self, session):
( j$ z8 C8 ^; n( F. q7 M6 T        body = {"unpause": None}
$ {# g0 I! T9 a/ U+ \+ X* b        self._action(session, body)
7 a4 r5 J1 e, D5 \( O3 w$ ?    def suspend(self, session):! k2 N, L# \" z
        body = {"suspend": None}
% Z* M: [& R0 L5 a1 ~% ?        self._action(session, body)8 c  a* P  H6 c3 _
    def resume(self, session):' k/ O  R4 C+ M
        body = {"resume": None}+ D, w+ o+ r% H% F1 M
        self._action(session, body)
" o* Q( e1 X* Z$ I7 w' r0 ~0 N$ P    def lock(self, session):
" i2 c8 f) ^9 w2 O        body = {"lock": None}
# y8 B& x+ X% w$ A$ l        self._action(session, body)* F. v! \+ b" A; g, m
    def unlock(self, session):
# _2 y# W! A2 E) x        body = {"unlock": None}; V$ J) _* g. Q8 Z. s5 ~$ w
        self._action(session, body)
! `; o% ^! g8 y    def rescue(self, session, admin_pass=None, image_ref=None):* W) g" T' s$ R/ q. H( k! P$ R# T3 v
        body = {"rescue": {}}  Q& W2 b4 R% k3 z
        if admin_pass is not None:
7 h6 T( E3 C. Q3 r            body["rescue"]["adminPass"] = admin_pass
5 ^/ e4 x7 h; P0 D2 _# [* x/ n! Y        if image_ref is not None:
! j1 v8 v; i4 _: g3 Q/ n# X8 Z            body["rescue"]["rescue_image_ref"] = image_ref) U) R, a. _. w+ K' Y
        self._action(session, body)
% z$ U! I3 C0 ?7 K8 U    def unrescue(self, session):
  u8 r+ j6 N( G- V2 j        body = {"unrescue": None}3 d" q2 R" ~- `/ \' x7 c+ k2 j$ I/ E8 D
        self._action(session, body)3 F; {& I/ q& K8 |+ u$ n) ?3 T6 {1 H- L
    def evacuate(self, session, host=None, admin_pass=None, force=None):
- D/ y7 P. f# s4 |        body = {"evacuate": {}}7 Y; D- ]1 ~$ {* ?
        if host is not None:/ ~' B# f4 w' U, t+ S5 Y8 W( S* f
            body["evacuate"]["host"] = host  m7 C( h4 F$ W
        if admin_pass is not None:
% t5 ]  O0 b( Z) ^" X% x& t9 c            body["evacuate"]["adminPass"] = admin_pass
% _) K$ F6 \7 ]; N7 a        if force is not None:; U4 P# T9 [8 t& D: e0 |3 c. P" q! n: H
            body["evacuate"]["force"] = force  n( O) @9 S, e  ~- l! z
        self._action(session, body)
5 J8 Z9 j# @& w! V9 x$ q' m    def start(self, session):. U' H. Y( p& E# q4 d# w' n
        body = {"os-start": None}# m3 l) j* y' k" t- X
        self._action(session, body), {8 x: \6 E8 U
    def stop(self, session):7 G2 v" y) X" K4 ~0 W$ R
        body = {"os-stop": None}; M! \; F0 Q0 {6 G% `7 P/ B; n; M% ^
        self._action(session, body)# V5 P/ q: B2 t
    def shelve(self, session):! j* r- ~3 j. K. x
        body = {"shelve": None}
+ g; O4 O# q" I8 m% b7 {9 K        self._action(session, body)
' i" ?8 M) M1 ]4 ]  w+ W" u    def unshelve(self, session, availability_zone=None):2 A7 l1 o7 S1 {5 N7 w
        body = {"unshelve": None}
, w# F( P% o! c9 z0 L& d" Z        if availability_zone:1 P$ i" C$ [9 D0 _5 }
            body["unshelve"] = {"availability_zone": availability_zone}7 Q& U% P: z  ]% I1 T0 A  T' q
        self._action(session, body)
3 a" }! f3 o2 Q# @: _( H1 V, b" G    def migrate(self, session):
, p  N2 g, e' A        body = {"migrate": None}( D' B2 P- l' E+ ]9 k/ J
        self._action(session, body)
2 s- _8 g1 X: c; S1 n5 B# [7 a    def get_console_output(self, session, length=None):* N0 R9 q( m  O
        body = {"os-getConsoleOutput": {}}6 ]9 A6 w# a6 E1 r
        if length is not None:* U' z/ C4 o3 ?$ c7 s& J
            body["os-getConsoleOutput"]["length"] = length6 h! s) u; g* w) J; A4 I
        resp = self._action(session, body)$ H4 u2 X: b  v/ k! R' z- Q- N
        return resp.json()
1 R& V3 |  l6 u1 a  b    def live_migrate(self, session, host, force, block_migration,
( Q4 c6 S2 U; @% o* W8 y                     disk_over_commit=False):" R7 ^0 o( u  |
        if utils.supports_microversion(session, '2.30'):- k  H2 |0 V% B+ Y1 v. b4 Z) F
            return self._live_migrate_30(; c3 `* o" T9 A, E
                session, host,
0 e' C8 q; B9 [0 e2 j                force=force,7 N/ d8 c0 d1 h# b: d" y
                block_migration=block_migration)
, h/ z+ F. y2 }7 n- p* O        elif utils.supports_microversion(session, '2.25'):
7 O( n' J+ H+ L3 F( r; c" p            return self._live_migrate_25(
3 z' h* v6 c9 t8 j                session, host,
# {; q6 {. I% `" v0 H                force=force,
3 U! Z* n# z6 Q& C0 x3 P3 N7 h) a                block_migration=block_migration)
3 c0 F6 ^. l1 {' o  x6 T8 b        else:
0 q9 D4 q+ a3 r5 n% h            return self._live_migrate(- I# e( P0 a) j, e' u  _) ^* ]) u
                session, host,
$ Z* @/ d  h- }' u: X+ J3 c                force=force,% T+ Z+ ~2 i% S# v
                block_migration=block_migration,' K/ u' l$ l( z0 m) K- {: v
                disk_over_commit=disk_over_commit)
1 e) s  u  d; ?* s! P, [. Q# J. t  T    def get_console_url(self, session, console_type):
* R9 i( u9 f* e2 }        action = CONSOLE_TYPE_ACTION_MAPPING.get(console_type)1 [* b  m% v- I9 A
        if not action:. K( \# ~+ a" b" m6 v7 Z# B( y  C
            raise ValueError("Unsupported console type %s" % console_type)4 S% P" X4 P" ^
        body = {action: {'type': console_type}}
) c" _; q; i7 {3 F  B+ \7 ?: F+ P        resp = self._action(session, body)- r# u) W5 E% d4 e0 L) q
        return resp.json().get('console')
4 ?( Q! N# e4 R6 P) I    def _live_migrate_30(self, session, host, force, block_migration):1 ?. F4 j& V7 C" X
        microversion = '2.30'
6 B" r' M# {& o1 Q. k        body = {'host': None}# F; i' A9 Y. M+ c/ r* a
        if block_migration is None:
0 n/ f% J2 G5 a5 g% q            block_migration = 'auto'
9 [* H; C4 \/ b        body['block_migration'] = block_migration9 `/ T; p+ a; U$ p7 L6 B8 `0 s& e
        if host:  j( }: S& S# R+ C: i- b" g5 e
            body['host'] = host6 G, @  y* w+ c' t! B- K& d
            if force:
" M* n7 |9 \# b- n                body['force'] = force3 }0 l0 x8 P1 l8 d" z/ w
        self._action(% k: V6 O* q4 ^/ {6 a
            session, {'os-migrateLive': body}, microversion=microversion)  f- U! n5 ~- G
    def _live_migrate_25(self, session, host, force, block_migration):) l! M7 \# R) P5 _; P1 a
        microversion = '2.25'
. ]9 I; P. H5 h. ~$ P; Q" M5 _3 F        body = {'host': None}
' H  e% G: n/ |1 q, S4 P        if block_migration is None:
- k0 H2 S( ]0 J0 C            block_migration = 'auto'
3 g( Y% S# F" V2 i& w- `        body['block_migration'] = block_migration- r# I2 B; I) B
        if host:/ h3 V' J4 x; |) Q8 [3 L8 p
            body['host'] = host; N; d& [; Q# r% l! ]# ?" |% t" K
            if not force:
5 x. c$ y% I  O4 }                raise ValueError(. r+ I7 P. L- H, B- p5 g( h
                    "Live migration on this cloud implies 'force'"  J: y  _6 y  O* l# @( _. G9 K4 Z
                    " if the 'host' option has been given and it is not"% {9 n2 g4 ]% F  n' m" j0 I% k
                    " possible to disable. It is recommended to not use 'host'"! u% G$ d+ S: ?% T5 t( \& m
                    " at all on this cloud as it is inherently unsafe, but if"" i) d# q* q3 \
                    " it is unavoidable, please supply 'force=True' so that it"
3 B  z% X7 W0 @( [                    " is clear you understand the risks.")
7 B# b7 i& [& B9 l) H7 ]        self._action(0 R8 M7 k1 E1 ]
            session, {'os-migrateLive': body}, microversion=microversion)5 R- o# `! M* ?5 i! L: K+ |  w# @
    def _live_migrate(self, session, host, force, block_migration,
9 d1 C3 m1 R$ z7 y4 r! t- d                      disk_over_commit):
$ L! z, X8 ?& S        microversion = None6 t$ t7 Z# C! O+ K! s
        body = {& P7 G  Q" _" q3 ?/ V9 x( @; t
            'host': None,: r0 d/ [/ z7 V* Y3 V. K
        }, x& t. _( `  \3 [" v
        if block_migration == 'auto':
1 h- l% D2 q8 T% X- F1 i- a0 x# K* N            raise ValueError(, j1 |) s4 y: t6 @: m
                "Live migration on this cloud does not support 'auto' as"7 @2 M4 t/ T$ A1 q' D2 c
                " a parameter to block_migration, but only True and False.")
5 _* h8 m1 @8 C0 r9 l        body['block_migration'] = block_migration or False/ S$ i$ x5 p7 ~0 f
        body['disk_over_commit'] = disk_over_commit or False$ Q# p6 i& r( n
        if host:
! K* W2 m. B! Y/ k            body['host'] = host2 U3 t0 C: K" S
            if not force:
' B6 J& s9 J  G3 A# p( y8 ^7 }                raise ValueError(( M% C+ U& j0 U2 J& i+ C
                    "Live migration on this cloud implies 'force'"
2 N# g) F$ P# G" A, I  n* O7 x& T/ T! p8 p& v                    " if the 'host' option has been given and it is not", H) O, j% y3 s+ h) A6 q6 j* h
                    " possible to disable. It is recommended to not use 'host'"
* S2 u* w! A& I3 i! N, ~6 `  M                    " at all on this cloud as it is inherently unsafe, but if"2 s4 a0 a9 l3 @* ]# F. M. H* g
                    " it is unavoidable, please supply 'force=True' so that it"
2 b( K% W+ A9 q) x, W                    " is clear you understand the risks.")7 g: |0 }# W% u& S% M5 J
        self._action(
0 k5 y6 F+ a, z# j# O            session, {'os-migrateLive': body}, microversion=microversion)- a& m( c, `* W6 _; z$ B
    def fetch_security_groups(self, session):
' P! o3 J( v6 z, {% n( x        """Fetch security groups of a server.
& V5 P( M5 N1 Z4 R9 _& E: @5 O        :returns: Updated Server instance.. O* _9 Y, L) W/ x  Q: t5 i, U& _
        """
6 n$ m# Q4 p; O5 Z        url = utils.urljoin(Server.base_path, self.id, 'os-security-groups')
6 r3 W8 A- j0 s  }+ S# ^        response = session.get(url)7 x2 c- m- ^( u
        exceptions.raise_from_response(response)( C  ?2 b' o1 T) u$ W  O% U
        try:7 K* `( f0 b# @" s
            data = response.json()- |2 g7 V: s& _6 ^' v
            if 'security_groups' in data:2 i" Z: j; l' ~  m1 k# a1 p6 P
                self.security_groups = data['security_groups']
" j  s8 s0 x. |. b. q        except ValueError:9 L, v4 Q# G4 B8 n+ L
            pass
! m# s* l  b5 J7 n        return self
& V2 A+ M! w" h: E& o+ A4 ^2 Z7 |& K6 k. I: O3 ~- c
ServerDetail = Server0 f' \8 Q2 b6 y) v. {" m, s' E  j
您需要登录后才可以回帖 登录 | 注册

本版积分规则

返回首页|Archiver|手机版|小黑屋|易陆发现技术论坛 ( 蜀ICP备2026014127号-1 )

GMT+8, 2026-6-12 02:02 , Processed in 0.025557 second(s), 22 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表