找回密码
 注册
查看: 1894|回复: 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. g$ ^* E- d  U2 H; X4 s9 Q0 O
# not use this file except in compliance with the License. You may obtain, k) m/ i0 m  W3 P# P& v! v
# a copy of the License at
4 |, Y! y$ k, f0 ~1 E" L#/ T; |/ n; o8 |+ f
#      http://www.apache.org/licenses/LICENSE-2.0, A- o# K! _2 d* c) Z4 z
#+ D& r3 ?% b: `% l
# Unless required by applicable law or agreed to in writing, software
* @$ Q/ u0 y. p6 k$ t- I6 D# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
& P, r/ S& u! b, z8 U, n& r) e3 ~# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the. ^6 q0 [; \9 e8 ?4 }% m, S
# License for the specific language governing permissions and limitations  z( Y0 ^& d7 P$ b& ]) L3 o
# under the License.
$ e! b) Q" f# l' s1 g  V4 qfrom openstack.compute.v2 import metadata- ]: M3 x6 `, J, e
from openstack import exceptions
# j' G% w; r+ w8 l0 e1 jfrom openstack.image.v2 import image8 P. v+ w9 [; O# h, ~4 F6 D; N& M
from openstack import resource
  M& N3 a. [5 Z5 p) i+ Efrom openstack import utils0 k1 u5 j4 ~7 P& {' w" C% H

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

本版积分规则

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

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

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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