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

- X- @" F" }2 C! @) aServerDetail = Server1 w; i: x5 H& b8 {
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-12 00:00 , Processed in 0.027988 second(s), 24 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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