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