|
|
openstack 云计算卷热扩容相关文档: Y; v) v) q2 w7 S
[toc]
2 _0 j* J/ Z8 x6 ]" z
* D1 I: D' H( b% Z- Y G- d指令
7 L2 @5 h" f. T! F# o8 Iusage: cinder extend <volume> <new_size>
* O5 E0 b! p0 P2 rAttempts to extend size of an existing volume.# F% q0 {4 B2 E; q( I5 j) x+ R
Positional arguments:& j' ^2 V' I2 V
<volume> Name or ID of volume to extend.% E) t% @: q* c! I, _# T
<new_size> New size of volume, in GiBs.2 i/ @: S. v4 B, ~0 s0 n
功能说明- r/ ^3 c+ n7 T V& Z+ f2 u
: ~, f$ B: P9 ?7 j' h+ N
最新的openstack()代码支持了热扩容功能:6 z. T z- [- e( q6 C K( I% L! u1 d
% V& p* O1 j. N& ~9 E& j) `% QAdd ability to extend 'in-use' volume. User should be aware of the
; m+ t- a. Y1 }9 ewhole environment before using this feature because it's dependent
# M: E) ~8 Q5 b8 K; Xon several external factors below:. o$ H4 D8 v6 n$ }
1. nova-compute version - needs to be the latest for Pike.
0 Q y9 ^/ A0 A; g: F5 n5 I2 E, _2. only the libvirt compute driver supports this currently.& ~8 P( { O H0 h
3. only iscsi and fibre channel volume types are supported
$ w6 p' {( M/ Lon the nova side currently." |$ Q- `- L) S* ~0 j
Administrator can disable this ability by updating the
; M( { y" S* e0 J& h7 D/ a1 ~'volume:extend_attached_volume' policy rule. Extend in reserved
0 j; m- ?* G% h+ ystate is intentionally NOT allowed.
! J; q% `) U2 P. l
7 n1 X3 V2 p9 `( l( L8 m' _5 E" {2 P注意:' `" l- J8 d% ]- ~( ?+ h9 |
1 z8 D+ O* V) z目前只有 libvirt 驱动能支持,且只有 iscsi 和 fibre channel 协议的卷类型能支持!$ T* f! h3 j" _) ~* P, W
cinder、nova做完热扩容后,fdisk -l查看可以看到磁盘大小变大,但是之前做的文件系统的大小是不会变化的,df -h 的结果跟扩容前一样。用户还必须用resize2fs 指令自行resize文件系统!
?# U1 p1 ^: U* F9 m' R《openstack 官网对于热扩容在 CINDER 方面的说明》% V/ c- U- Z e
3 ]1 o0 @, @ V# |《openstack 官网对于热扩容在 NOVA 方面的说明》- Q1 `/ [! i" B4 M' x9 J
, ]! j: i) q' ` `" |* QCinder方面代码6 q4 y' U4 {4 q3 K) `* S
Cinder 方面其实没有什么改动,跟冷扩容做的事一样,扩容后的size必须是整数GB且大于扩容前size。核心实现是调用卷驱动里的方法,如netapp,调用的是cinder.volume.drivers.netapp.dataontap.client.client_base.Client#do_direct_resize实现。唯一的区别是,扩容后,会调用nova api。
% m8 A! M( P# I: }" q' q) I9 n! G2 W, N4 E$ K0 V3 s
cinder.api.contrib.volume_actions.VolumeActionsController#_extend:& I9 w/ t" |, L9 V
# 如果版本是 且 卷状态是in-use,则走热扩容
1 q3 }# j% ^- I# X# cif req_version.matches("3.42") and volume.status in ['in-use']:
2 L# A8 p4 I- e: S7 D; c self.volume_api.extend_attached_volume(context, volume, size)$ f' B4 q$ l" e
else: # 否则,冷扩展
0 p3 \' C# A" U/ |: H3 y. @' z/ ~ self.volume_api.extend(context, volume, size)
% I/ r. c6 }/ g# ?% U7 S7 T0 `: l* T' Q" Z, u- z3 l
cinder/volume/manager.py里调用nova api,通知nova要扩展卷:- H% K4 J: x& a5 {6 [) k
if orig_volume_status == 'in-use':
( R% c( f4 k M+ @ nova_api = compute.API()
' T8 c$ \% O& C( ]+ I% h' R ~ instance_uuids = [attachment.instance_uuid4 L4 o' j0 R. Z9 ?; L
for attachment in attachments]
; |! }& `6 J. S$ Z4 \0 ]& c7 x$ G nova_api.extend_volume(context, instance_uuids, volume.id)
0 v1 S* R, w7 c" m/ Y! C6 h$ ?5 m这里调用nova client 里的:8 J3 i# k7 n3 X( h' D3 Z7 n9 r
novaclient.v2.server_external_events.ServerExternalEventManager#create:
& U& J; L7 g$ D* t' C; `+ O2 z8 @+ j( V4 i3 X
class ServerExternalEventManager(base.Manager):* Y# }2 D, M1 d9 q5 z' i3 P
resource_class = Event) w% k$ ? i/ K' D5 a- e4 C
: [. S s. d( P3 O% ^: @
def create(self, events):, P2 m0 `5 W% n2 E" u$ L, S r
"""Create one or more server events.
7 d( H3 z- p7 t! z. M- d( U7 K+ r' {# j; }" v- m
:param:events: A list of dictionaries containing 'server_uuid', 'name',* G/ ]' N: u+ y d$ h# e
'status', and 'tag' (which may be absent)
2 |+ `. z' n7 l- t """
; H: F: ^3 Y: d1 c2 L
0 y7 ~, X, E6 n2 f) s& p# B body = {'events': events}4 }- d3 I4 s. {- K
return self._create('/os-server-external-events', body, 'events',7 \& T5 v$ b% J& R
return_raw=True)
# D3 M7 R. N/ y! h% v# T1 `. O+ J- E" }0 y
'events':
$ X8 w0 R+ Q- c @3 F8 N4 V; Q# j% R' D
{'name': 'volume-extended',
2 l4 f/ O8 b& v( S'server_uuid': server_id,
+ i. R3 r8 q! z7 b'tag': volume_id}0 V+ A+ C8 U, q' r. R5 k4 z
Nova方面代码. i$ o# P# S& _9 H0 k. l
Nova将使用现有的外部事件API端点监听来自Cinder的附加卷扩展通知。收到通知后,Nova将使用os-brick触发主机上的设备重新扫描,以发现卷大小的变化。 K3 \" k, W' u. o
$ @6 Y3 S! i; j3 t$ ^
nova api '/os-server-external-events':
" W) L5 e; [5 o8 N% X! {
+ d. V7 x9 a! g) a! z! e9 Yrequest:
- l0 Z9 a5 @4 s7 r8 y+ O {
- L4 f1 ]% `* |/ y- |# x* ? "events": [; u- z' a) _! L& g2 Q% ?1 A2 y' [
{
# ^5 b l8 O- W7 D: P "name": "volume-extended",/ N0 {, W D0 N# T( V: T4 t1 F" w
"server_uuid": "3df201cf-2451-44f2-8d25-a4ca826fc1f3",
! Y; C, W" e+ W6 n: ~3 [; s "tag": "0e63d806-6fe4-4ffc-99bf-f3dd056574c0"; B& M0 f& S" @$ @+ m+ q
}1 N! f6 U8 e( y5 x6 q
]7 V" r4 s0 [2 _+ ~
}
9 G% k/ F0 U0 ~1 R8 Q7 g9 nresponse:+ K3 u! T9 `$ K& s7 C: L- P" o
{
) f8 z! i: `) U6 Y; y0 C "events": [' W( K* r; q: y) ?" p0 b
{
( r3 E3 H$ a- i1 t. N2 Y# M+ D! V8 N "name": "volume-extended",2 Q2 R2 [( W @' C% M) i/ V5 x; M5 f, u
"status": "completed",
5 C' e2 k0 E- B! v "code": 200,2 @( b& i7 A% ^8 i4 R/ `6 B# C
"server_uuid": "3df201cf-2451-44f2-8d25-a4ca826fc1f3",$ m5 k1 @' u& ?$ i# Q' J5 e+ j, e
"tag": "0e63d806-6fe4-4ffc-99bf-f3dd056574c0"
) ~! |. Q# H- N3 a# t M* X- _+ X }/ H) C8 @# h8 k0 t6 P5 y
]4 f, Z# [; w. }) O
}
5 A* A! d. M$ L& O# k检查nova驱动compute_driver = libvirt.LibvirtDriver是否能支持卷扩展 "supports_extend_volume": True
" B$ [! c3 Q" G0 m$ f( k驱动的支持功能定义在nova.virt.libvirt.driver.LibvirtDriver:
3 G! c: A2 D& t* w7 a& e, N) \class LibvirtDriver(driver.ComputeDriver):
7 h' ?2 B5 `* @. n5 w4 L8 W capabilities = {' f# D3 l$ M" t8 o. d l
"has_imagecache": True,: I" L Z0 L4 H% k3 k ?
"supports_recreate": True,4 U7 Z6 t6 D, x6 } b
"supports_migrate_to_same_host": False,
: b! a" V7 t; G "supports_attach_interface": True,
$ X+ \/ {8 N6 U2 w6 C "supports_device_tagging": True,
% m% M5 D9 T" i& o4 P "supports_tagged_attach_interface": True,
# g9 P- R& J5 C! X W9 Z- `! P "supports_tagged_attach_volume": True,' d l' H. P7 c t$ K+ v8 h
"supports_extend_volume": True,1 u3 l- `; B" K- @- Z! \
}
1 [3 X' | z( E! p, ?
s" b* U" d! j& i1 J8 k9 S根据卷connection_info找对应的驱动,然后调用驱动的extend_volume方法。
; ~, K# e& a6 g0 q6 r def _extend_volume(self, connection_info, instance):! b. R2 x" Z2 w6 B5 i2 {: A
vol_driver = self._get_volume_driver(connection_info)7 U3 w% I0 W |2 _2 H$ D
return vol_driver.extend_volume(connection_info, instance)! A& t. l" a3 J: J
比如Iscsi,就会找nova.virt.libvirt.volume.iscsi.LibvirtISCSIVolumeDriver#extend_volume:* l: w S5 E* \% q7 s) P$ u
new_size = self.connector.extend_volume(connection_info['data'])
. h5 Z0 o. K1 f9 M4 y- v& F4 `1 z在调用ISCSIConnector的extend_volume" U5 a# q4 a2 N6 `+ j) ?/ t8 ]2 ]! N
os_brick.initiator.connectors.iscsi.ISCSIConnector#extend_volume:
3 s# |+ s+ A9 E' o X% ^6 h volume_paths = self.get_volume_paths(connection_properties)" u3 H' u J. V; I* p3 M+ k
if volume_paths:2 J. v& F* p1 k! e
return self._linuxscsi.extend_volume(volume_paths)0 L3 l# P3 T; D; N8 M
调用linuxscsi的extend_volume
5 [- O' E- j/ J) gos_brick.initiator.linuxscsi.LinuxSCSI#extend_volume:
& q, Z, h8 m* z. W3 A def extend_volume(self, volume_paths):
' Y, f/ J& j! B( x' l) g """Signal the SCSI subsystem to test for volume resize.
. G5 M, Z. _* k0 p; m- Y# J) {3 w, E
This function tries to signal the local system's kernel4 T$ G, Q3 s0 ?4 p6 B, r2 O7 }/ N
that an already attached volume might have been resized.3 M' R% C) \9 I; Q( V# r
"""
9 F' H* d$ Y, j& z LOG.debug("extend volume %s", volume_paths)
2 q8 F9 O- Q3 i0 U( S
, t* y& [6 g& t for volume_path in volume_paths:$ T$ \ n& N9 |$ E1 J/ b
device = self.get_device_info(volume_path)
* |9 f9 a0 X3 B7 \$ k5 Q% m7 X$ A LOG.debug("Volume device info = %s", device)
3 P. \ ]& q$ ~ I5 t, `0 z device_id = ("%(host)s:%(channel)s:%(id)s:%(lun)s" %
5 T3 f, ^$ C# y/ C {'host': device['host'],
- j- [$ W0 M% g: t% H% r 'channel': device['channel']," ^ [3 P& ?& \2 w( w' y& B
'id': device['id'],! E- ^7 _2 ^7 x; [4 n
'lun': device['lun']})4 [4 N& G" C! O% V( q
7 t1 \! y$ a1 y4 y s* g1 l scsi_path = ("/sys/bus/scsi/drivers/sd/%(device_id)s" %: d& _+ r" G" E8 ]+ X
{'device_id': device_id})
) d; F9 d. z2 I$ g0 ]9 b # 如:scsi_path = u'/sys/bus/scsi/drivers/sd/3:0:0:3'
: i" X$ o9 W8 O% w% L, `& K5 P7 v- q- U$ l- j$ G
# 获取设备大小
Q2 f6 I3 B( C' p' K' U; u5 |% ^ size = self.get_device_size(volume_path)3 R7 \, [2 _3 y5 W0 p2 H: m
LOG.debug("Starting size: %s", size)
/ g% G1 g9 D7 O0 m5 G ~2 j5 A, E5 e+ v' H% o
# now issue the device rescan, @: O; {0 L- |# P. b
rescan_path = "%(scsi_path)s/rescan" % {'scsi_path': scsi_path}! b# Y5 Q5 C: c' Z$ n; O
# 如:rescan_path = u'/sys/bus/scsi/drivers/sd/3:0:0:3/rescan'1 ]( Z; u! a2 h# j7 \7 M
, s; i5 b3 N, T' c1 S% n
# 在rescan_path文件里写入1。$ I! d2 h( `+ c. K. ?" Q
# 对于SCSI设备,对 rescan 文件设置为1 可以使SCSI设备重新扫描。' G' }9 c; E p. o' c6 g
self.echo_scsi_command(rescan_path, "1")1 i% f: B! d4 u. B2 i; o) W
new_size = self.get_device_size(volume_path)( a# d9 S1 j1 C6 w2 E$ M
LOG.debug("volume size after scsi device rescan %s", new_size)
$ [: {' Q- r9 P, N# H2 p6 |9 _9 C5 e
# 通过指令`/lib/udev/scsi_id --page 0x83 --whitelisted /dev/disk/by-path/ip-12.24.3.10:3260-iscsi-iqn.1992-08.com.netapp:sn.2d72abb030d511e7875800a098ac0ce9:vs.24-lun-3 `拿到wwnid
- n9 a4 ^3 J- B: {- Q) ?* ] scsi_wwn = self.get_scsi_wwn(volume_paths[0])
( s$ \3 r" X( o/ Q7 J # 如:scsi_wwn = u'3600a09803830387461244a62344f6b52'
+ F% i' o" z( n+ Q8 S mpath_device = self.find_multipath_device_path(scsi_wwn)
( x/ F! K: |/ V3 s2 w8 P9 _* ?3 o. c # 如果mpath_device非空,说明是多路径磁盘。
- @ ]' Y3 D% A: _ if mpath_device:: ?) l6 _ O7 a0 D5 [
# Force a reconfigure so that resize works0 ^; B+ i+ h: b' K s) }
# 执行 `multipathd reconfigure` 重新获取 multipath.conf 配置中变化。
! B7 B- x! ], |, `! ~ self.multipath_reconfigure()
6 I1 R( s$ e9 z- ]9 U0 |. U# { # 获取设备大小2 L+ p; V# L: }; r- d* T7 B9 ]
size = self.get_device_size(mpath_device)5 W7 E: n" h A* b" D4 Z! ]. R
LOG.info("mpath(%(device)s) current size %(size)s",- p- S: T9 f& F* v4 p
{'device': mpath_device, 'size': size})/ x ]2 D$ p- f# O# y, K( _! y
3 p; j% s! V9 g! }* e' @. L # 调用指令 multipathd resize map multipath_device 重置多路径磁盘的大小
3 m. R/ j4 J& b; h" a% R result = self.multipath_resize_map(scsi_wwn)
. g9 ^' J/ l3 c4 }& l if 'fail' in result:
( q) l- E* N$ s: o! n LOG.error("Multipathd failed to update the size mapping of "0 e' T0 H4 f1 s( w @
"multipath device %(scsi_wwn)s volume %(volume)s",' z) ~9 \5 n( h+ O
{'scsi_wwn': scsi_wwn, 'volume': volume_paths})
* f0 K' r% M0 V5 W1 { return None- S+ t" @; f0 Y/ P* b
) Q+ p, N4 J' d
# 获取设备大小- F @/ A/ r' g' l/ |
new_size = self.get_device_size(mpath_device) e' @& U1 J/ o
LOG.info("mpath(%(device)s) new size %(size)s",8 w# L# |: g& R1 `8 ^
{'device': mpath_device, 'size': new_size})6 n" n( V6 k a' \
2 _/ |2 X! ^- G+ g8 e% Y return new_size
! \7 m O2 \ Q; B流程简要:: g7 g- o" K! `
向scsi扫描文件写入 1:tee -a 1 "%(scsi_path)s/rescan",对于SCSI设备,对 rescan 文件设置为1 可以使SCSI设备重新扫描。
6 l' c6 a0 t* Z% ] d) V2 q
- a: z( y* J! g6 B) q4 P/lib/udev/scsi_id --page 0x83 --whitelisted 得到scsi_wwn) a$ y% P: l$ k' K
8 B2 f- N7 \. p4 d1 l
检查多路径设备,先检查/dev/disk/by-id/dm-uuid-mpath-%(wwn)s是否存在,存在则返回路径;如果不存在,则检查/dev/mapper/%(wwn)s,存在则返回路径,不存在返回none。
' M" O7 U) N0 P; t; }9 c" a8 {
' i) e( f* |; k, D5 t; ~1 ?! w& c如果第3部结果为none,流程结束。
$ H& C4 e' K `
0 S: x1 O$ ]- q+ B' e5 T* P6 n如果第3步查到路径,则需要执行 multipathd reconfigure 重新获取 multipath.conf 配置中变化。2 U; M5 C7 N$ X" |
# R+ R$ |8 I( d- l
执行multipathd resize map scsi_wwn 重新设置设备大小。多路径磁盘流程结束。
$ o! \5 h" u& A( j% E- {- [: E8 c, W( M2 J1 A3 C2 W
FC和Iscsi驱动都是这样流程。
* G3 x. R! ^6 N- _/ q7 i% M4 @# W5 a4 t! B v$ V
附:
; r% }% g! P* F6 r/ Tcentos 如何使用多路径磁盘
4 `+ _: J! }% E1.安装multipath工具
' z) Q, R+ @+ k# 安装multipath工具4 f" ^3 f$ }; Y0 O, b5 C+ B
yum -y install device-mapper device-mapper-multipath
/ Y8 i2 }+ E. i6 [5 a+ Z5 ^. a' [2 B* Y* m& }
#加载multipath内核模块
" j" k2 J1 |! b- O0 G& a. dmodprobe dm-multipath
/ T! v7 e$ Q* ~) Q* c. Z0 Rmodprobe dm-round-robin$ a4 r0 q0 s Z4 U
7 I- o* D* m; d) E# u; J# 拷贝默认配置文件
' ^$ I0 w) ?4 j3 Q7 Z4 }4 @3 b: Ncat /usr/share/doc/device-mapper-multipath-0.4.9/multipath.conf > /etc/multipath.conf) F9 }- S" H. @/ ~" |( C
5 F8 ]5 C9 A/ Q$ t#启动服务
# w6 M c+ V$ l$ P& E8 gservice multipathd start 或者 /bin/systemctl start multipathd.service
. o0 h: H( a# {( J/ Q4 t$ G7 E& {7 |2.配置nova_compute的配置文件( {% K) C) R+ W0 U- A5 d5 }
[libvirt]
& J! f4 x R. Q' x7 l# iscsi磁盘是否开启多路径,True为开启9 }5 O/ B- X: n; G% ^; ]
volume_use_multipath=True0 O Y7 R2 j& l3 @! ~
2 F4 d2 x! i. V) ~# 建立连接时的rescan次数' E# P3 Y# S1 Q$ m& H2 a
num_iscsi_scan_tries=5
+ T7 F+ N0 S s+ L) V4 Z2 n" d% y2 L& E; z0 Z/ z. k& Q2 {$ ]
# 创建iscsi连接使用的iface名称
" M9 x) s1 r' s' Eiscsi_iface=default
# j+ L6 R5 F7 f3 |/ w修改在线多路径设备容量的方法
. X D: J3 X% L6 x0 f9 jredhat官网上介绍了修改在线多路径设备容量的方法:; E6 e" Q" E% \6 k+ Q0 F. G
7 e2 J, o: a- z {RESIZING AN ONLINE MULTIPATH DEVICE
2 ]; l; }- P* ?$ SIf you need to resize an online multipath device, use the following procedure.
9 _, U5 c0 n$ u
- c1 `, d& V& }: g- v( |Resize your physical device.
3 _" o) X+ |; n: l2 nExecute the following command to find the paths to the LUN:0 K+ s9 H% o- a2 ]+ O$ R }
# multipath -l. C& {7 Z/ Q9 T' s
! K- K" p' i% \$ a5 z$ A
Resize your paths. For SCSI devices, writing a 1 to the rescan file for the device causes the SCSI driver to rescan, as in the following command:
1 B# l6 u. X! ?) _6 u# echo 1 > /sys/block/path_device/device/rescan( h& G1 y4 }4 Y) C
& A6 T4 L+ s% p% I, t. H( {Ensure that you run this command for each of the path devices. For example, if your path devices are sda, sdb, sde, and sdf, you would run the following commands:$ m* W& {$ O+ {- A6 R) E: r. |
/ {3 t6 }$ m8 ^3 m: {3 W
# echo 1 > /sys/block/sda/device/rescan$ V( _$ |3 I2 b3 j$ x) E1 H
4 Y) X7 K. K3 s+ f* Y6 X6 x Y
# echo 1 > /sys/block/sdb/device/rescan
- T" ^4 `' w6 H- d: S/ M/ i+ b0 i$ H
# echo 1 > /sys/block/sde/device/rescan" E: |1 c8 {8 C
; ^# ]5 N8 B \' L: ^9 _# echo 1 > /sys/block/sdf/device/rescan5 ^, d, H: d+ W
7 W) G) _: y* l; C# ?/ S
Resize your multipath device by executing the multipathd resize command:
, R, U1 K0 L9 a$ o# M6 l# multipathd resize map multipath_device
; n1 D7 J" Y. r% h4 g- \
9 S4 J v* @! c4 D) V/ MResize the file system (assuming no LVM or DOS partitions are used):) l( M# J: z) _; @8 @% O4 I2 l
# resize2fs /dev/mapper/mpatha ]9 h- i4 U" E' {8 r. V; j$ X
* Z' {& E4 V& e3 g7 Q; p6 ^ O看的出,跟nova的处理流程差不多,主要少了resize2fs一步。
' R' w7 M7 w* p8 Q5 E3 x
% F6 Q, t, L& I0 d) P A测试过程遇到问题+ s. Q1 y6 }1 ~7 B: g
问题: 创建虚机时提示"Host 'localhost.localdomain' is not mapped to any cell"
7 l' F9 w8 X# f9 Z$ ^
o. M3 l- [& [) ]1 h2 o; m: C解决: 执行 nova-manage cell_v2 simple_cell_setup
+ w+ V4 S2 \% V& c5 `5 @4 I
2 \* `! P G! ?问题: n-cpu日志提示libvirt 版本过低
' h4 k. R) _, a8 y+ g
; }2 K1 T/ [9 x( z! t/ T解决: 按照以下步骤升级$ l2 P# H, L9 j% i
$ rpm -qa|grep kvm* k, \ r* B0 R9 N: N% O" k2 ]
$ rpm -qa|grep qemu-system
+ I. n* o5 o9 E3 t1 P: [- k$ sudo virsh -c qemu:///system version --daemon: d3 h* }* ?9 h8 c3 B
$ sudo yum remove qemu-system-x86& b$ e, Y& z( E0 a/ |& Y
$ sudo service libvirtd restart. b2 U3 ?+ `& c1 R' h
$ sudo virsh -c qemu:///system version --daemon) ^: ?7 E+ k, v: `
# V) A/ w1 r5 K
问题: c-vol 提示:ERROR oslo_messaging.rpc.server ConnectionFailedError: HTTPConnectionPool(host='12.24.2.18', port=2379):( Y: u+ O- i$ l; x q' p5 m: Z" J
Max retries exceeded with url: /v3alpha/lease/grant (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x718cc10>:) U. }' G/ V7 o
Failed to establish a new connection: [Errno 111] ECONNREFUSED',))* M. r( t' V3 `! N3 n
ERROR oslo_messaging.rpc.server
, T) Q$ R+ O* t! V( T: \7 G) b7 N
# D) Z, O+ f; G5 g$ g% ^8 m4 T解决: 配错了coordination。在cinder.conf里吧[coordination]的backend_url注释掉即可。6 N6 _$ H9 U# t( G1 U
. T7 }& G: Y& O1 z) j
4 m. j Z7 x' E- ]
|
|