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

- L! B' x9 C/ I, {ServerDetail = Server7 M: I% m5 ^* E5 O
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-12 01:05 , Processed in 0.017506 second(s), 22 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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