标签:
因为对团队的环境部署和持续集成还不了解,先使用最简单的方式安装个开发环境。过程总结如下。
通常如果只是搭一套环境自己用的话,使用CentOS的RDO方式搭建all-in-one的openstack开发环境是最快的。(RDO是什么?https://www.rdoproject.org/)
但是如果考虑持续集成、部署和升级的话,肯定需要专门的工具来打包、安装二次开发的代码,通常使用fuel或者TripleO,结合puppet来部署。可以实现快速部署和自动化测试。
另外:大家用的开发环境可以克隆如下虚拟机,方式如下:
公司环境:
10.33.41.55上的allinone快照hcloud大家可以用一下,admin 123456
虚拟机启动后需要改几个配置,改完重启下
vi/etc/network/interfaces
address192.168.1.108 ----改成自己的fix-ip
vi/etc/nova/nova.conf
my_ip =192.168.1.108 ----改成自己的fix-ip
novncproxy_base_url= http://10.33.41.123:6080/vnc_auto.html ----改成自己的floating-ip
在自己的虚拟机上创建ceph集群可能会很卡,最好在10.33.41.55上创建
海康源:http://mirrors.hikvision.com.cn
想用什么ISO可以从这里下载,不用再去网上下,内部源很快。
http://mirrors.hikvision.com.cn/centos/7/
http://mirrors.hikvision.com.cn/centos/7/cloud/x86_64/openstack-kilo/
yum源配置:
解压,将/etc/yum.repos.d替换掉
执行如下命令安装:
yum clean all; yum makecache; yum update
yum install -y openstack-packstack
首先创建一个vg:cinder-volumes
[root@localhost yum.repos.d]# pvcreate/dev/sdb
Physical volume "/dev/sdb" successfully created
[root@localhost yum.repos.d]# vgcreatecinder-volumes /dev/sdb
Volume group "cinder-volumes" successfully created
[root@localhost yum.repos.d]# vgs
VG #PV #LV #SNAttr VSize VFree
cinder-volumes 1 0 0wz--n- 30.00g 30.00g
[root@localhost yum.repos.d]#
然后开始部署:
packstack --install-hosts= 192.168.129.131(只要将这个IP改为你的IP即可)
Enter the path to your ssh Public key toinstall on servers:输入/root/.ssh/id_rsa.pub
这几个选项注意:
Should Packstack install OpenStack ObjectStorage (Swift) [y|n] [y] : n
Should Packstack install Nagios to monitorOpenStack hosts [y|n] [y] : n
Should Cinder‘s volumes group be created(for proof-of-concept installation)? [y|n] [y] : n
Would you like to provision for demo usageand testing [y|n] [y] : n
其他一路回车,然后到Proceedwith the configuration listed above? (yes|no): 输入yes
等待部署成功,时间会比较久:
最后成功时的界面:
整个过程的日志:
如果中间出错,可能是源漏配或者配置文件写错,在部署过程中出现的问题都是这个原因。
所有问题都会有日志,可以看下,具体问题具体分析。
以下可以不看,遇到问题再参考:
1. 第一次执行packstack --install-hosts= 192.168.129.130可能需要输入路径:
Enter the path to your ssh Public key to install on servers:输入/root/.ssh/id_rsa.pub
2. Nagios默认可能是y,先不安装
ERROR :Error appeared during Puppet run: 192.168.129.130_nagios.pp
Error: /Stage[main]/Main/Exec[nagios-plugins-ping]/onlyif:Check "yum install -y -d 0 -e 0 nagios-plugins-ping &> /dev/null&& exit 1 || exit 0" exceeded timeout
可以先不安装nagios,选择的时候注意下
Should Packstack install Nagios to monitor OpenStack hosts[y|n] [y] : n
3. 一般一次不会成功,可以重复执行
4. 如果选择provision for demo usage and testing需要上传cirros到glance,这里选N:
ERROR : Error appeared during Puppet run: 192.168.129.130_provision_glance
Error:Could not prefetch glance_image provider ‘glance‘: Execution of‘/usr/bin/glance --os-tenant-name services --os-username glance --os-password0fc686dc221447e0 --os-region-name RegionOne --os-auth-urlhttp://192.168.129.130:35357/ image-show b388a764-6746-4303-8b27-2af7df743b92‘returned 1: id
You will find full trace in log/var/tmp/packstack/20160419-233514-TdAahP/manifests/192.168.129.130_provision_glance.log
查看日志:
Error:Could not prefetch glance_image provider ‘glance‘: Execution of‘/usr/bin/glance --os-tenant-name services --os-username glance --os-password0fc686dc221447e0 --os-region-name RegionOne --os-auth-urlhttp://192.168.129.130:35357/ image-show b388a764-6746-4303-8b27-2af7df743b92‘returned 1: id
Notice:/Stage[main]/Main/Glance_image[cirros]/ensure: created
Notice: Finished catalog run in 4.20 seconds
应该是预取cirros镜像失败,重新部署
Enter the location of an image to be loaded intoGlance [http://download.cirros-cloud.net/0.3.3/cirros-0.3.3-x86_64-disk.img] :
这个地址是可以访问的,但是cirros镜像的下载非常慢非常慢,所以会超时
解决:“
Would you like to provision for demo usage and testing[y|n] [y] : n
5. 最后出现:
192.168.129.130_cinder.pp: [ ERROR ]
Applying Puppet manifests [ ERROR ]
ERROR : Error appeared during Puppet run:192.168.129.130_cinder.pp
Error: cinder type-create iscsi returned 1instead of one of [0]
You will find full trace in log/var/tmp/packstack/20160420-003150-z8S4uC/manifests/192.168.129.130_cinder.pp.log
Please check log file /var/tmp/packstack/20160420-003150-z8S4uC/openstack-setup.logfor more information
日志:
Error: cinder type-create iscsi returned 1 instead of one of [0]
Error:/Stage[main]/Main/Cinder::Type[iscsi]/Exec[cinder type-create iscsi]/returns:change from notrun to 0 failed: cinder type-create iscsi returned 1 instead ofone of [0]
Notice:/Stage[main]/Main/Cinder::Type[iscsi]/Cinder::Type_set[lvm]/Exec[cindertype-key iscsi set volume_backend_name=lvm]: Dependency Exec[cinder type-createiscsi] has failures: true
Warning:/Stage[main]/Main/Cinder::Type[iscsi]/Cinder::Type_set[lvm]/Exec[cindertype-key iscsi set volume_backend_name=lvm]: Skipping because of faileddependencies
Notice:/Stage[main]/Cinder::Volume/Service[cinder-volume]: Triggered ‘refresh‘ from 3events
Notice:/Stage[main]/Cinder::Scheduler/Service[cinder-scheduler]: Triggered ‘refresh‘from 3 events
Notice: Finished catalog run in 9.46seconds
处理方法:
此处选择n试试:
Should Cinder‘s volumes group be created(for proof-of-concept installation)? [y|n] [y] : n
Would you like to provision for demo usageand testing [y|n] [y] : n
所有选项:
出错:
Checking if the Cinder server has acinder-volumes vg[ ERROR ]
ERROR : The cinder server should contain acinder-volumes volume group
创建一个vg:cinder-volumes
[root@localhost yum.repos.d]# pvcreate/dev/sdb
Physical volume "/dev/sdb" successfully created
[root@localhost yum.repos.d]# vgcreatecinder-volumes /dev/sdb
Volume group "cinder-volumes" successfully created
[root@localhost yum.repos.d]# vgs
VG #PV #LV #SNAttr VSize VFree
cinder-volumes 1 0 0wz--n- 30.00g 30.00g
[root@localhost yum.repos.d]#
重新搞:
Should Packstack install OpenStack ObjectStorage (Swift) [y|n] [y] : n
Should Packstack install Nagios to monitorOpenStack hosts [y|n] [y] : n
Should Cinder‘s volumes group be created(for proof-of-concept installation)? [y|n] [y] : n
Would you like to provision for demo usageand testing [y|n] [y] : n
日志记录:
[root@localhost yum.repos.d]# packstack --install-hosts= 192.168.129.131 Welcome to the Packstack setup utility The installation log file is available at: /var/tmp/packstack/20160420-020826-gfsofC/openstack-setup.log Enter the path to your ssh Public key to install on servers [/root/.ssh/id_rsa.pub] : Enter a default password to be used. Leave blank for a randomly generated one. : Confirm password : Should Packstack install MariaDB [y|n] [y] : Should Packstack install OpenStack Image Service (Glance) [y|n] [y] : Should Packstack install OpenStack Block Storage (Cinder) service [y|n] [y] : Should Packstack install OpenStack Shared File System (Manila) service [y|n] [n] : Should Packstack install OpenStack Compute (Nova) service [y|n] [y] : Should Packstack install OpenStack Networking (Neutron) service [y|n] [y] : Should Packstack install OpenStack Dashboard (Horizon) [y|n] [y] : Should Packstack install OpenStack Object Storage (Swift) [y|n] [y] : n Should Packstack install OpenStack Metering (Ceilometer) [y|n] [y] : Should Packstack install OpenStack Orchestration (Heat) [y|n] [n] : Should Packstack install OpenStack Clustering (Sahara) [y|n] [n] : Should Packstack install OpenStack Database (Trove) [y|n] [n] : Should Packstack install OpenStack Bare Metal (Ironic) [y|n] [n] : Should Packstack install OpenStack client tools [y|n] [y] : Enter a comma separated list of NTP server(s). Leave plain if Packstack should not install ntpd on instances.: Should Packstack install Nagios to monitor OpenStack hosts [y|n] [y] : n Enter a comma separated list of server(s) to be excluded. Leave plain if you don't need to exclude any server.: Do you want to run OpenStack services in debug mode [y|n] [n] : Enter the IP address of the controller host [192.168.129.130] : Enter list of IP addresses on which to install compute service [192.168.129.130] : Enter list of IP addresses on which to install network service [192.168.129.130] : Do you want to use VMware vCenter as hypervisor and datastore [y|n] [n] : Enable this on your own risk. Do you want to use unsupported parameters [y|n] [n] : Should interface names be automatically recognized based on subnet CIDR [y|n] [n] : To subscribe each server to EPEL enter "y" [y|n] [n] : Enter a comma separated list of URLs to any additional yum repositories to install: To enable rdo testing enter "y" [y|n] [n] : To subscribe each server to Red Hat enter a username : To subscribe each server with RHN Satellite enter RHN Satellite server URL: Enter the filename of the SSL CAcertificate, if the CONFIG_SSL_CACERT_SELFSIGN is set to y the path will be CONFIG_SSL_CERT_DIR/certs/selfcert.crt [/etc/pki/tls/certs/selfcert.crt] : Enter the filename of the SSL CAcertificate Key file, if the CONFIG_SSL_CACERT_SELFSIGN is set to y the path will be CONFIG_SSL_CERT_DIR/keys/selfkey.key [/etc/pki/tls/private/selfkey.key] : Enter the path to use to store generated SSL certificates in [~/packstackca/] : Should packstack use selfsigned CAcert. [y|n] [y] : Enter the selfsigned CAcert subject country. [--] : Enter the selfsigned CAcert subject state. [State] : Enter the selfsigned CAcert subject location. [City] : Enter the selfsigned CAcert subject organization. [openstack] : Enter the selfsigned CAcert subject organizational unit. [packstack] : Enter the selfsigned CAcert subject common name. [localhost.localdomain] : Enter the selfsigned CAcert subject admin email. [admin@localhost.localdomain] : Set the AMQP service backend [qpid|rabbitmq] [rabbitmq] : Enter the IP address of the AMQP service [192.168.129.130] : Enable SSL for the AMQP service? [y|n] [n] : Enable Authentication for the AMQP service? [y|n] [n] : Enter the IP address of the MariaDB server [192.168.129.130] : Enter the password for the MariaDB admin user : Confirm password : Enter the password for the Keystone DB access : Confirm password : Enter y if cron job for removing soft deleted DB rows should be created [y|n] [y] : Confirm password [y|n] [y] : Region name [RegionOne] : Enter the email address for the Keystone admin user [root@localhost] : Enter the username for the Keystone admin user [admin] : Enter the password for the Keystone admin user : Confirm password : Enter the password for the Keystone demo user : Confirm password : Enter the Keystone service name. [keystone|httpd] [keystone] : Enter the Keystone identity backend type. [sql|ldap] [sql] : Enter the password for the Glance DB access : Confirm password : Enter the password for the Glance Keystone access : Confirm password : Glance storage backend [file|swift] [file] : Enter the password for the Cinder DB access : Confirm password : Enter y if cron job for removing soft deleted DB rows should be created [y|n] [y] : Confirm password [y|n] [y] : Enter the password for the Cinder Keystone access : Confirm password : Enter the Cinder backend to be configured [lvm|gluster|nfs|vmdk|netapp] [lvm] : Should Cinder's volumes group be created (for proof-of-concept installation)? [y|n] [y] : n Enter Cinder's volumes group usable size [20G] : Enter y if cron job for removing soft deleted DB rows should be created [y|n] [y] : Confirm password [y|n] [y] : Enter the password for the Nova DB access : Confirm password : Enter the password for the Nova Keystone access : Confirm password : Enter the CPU overcommitment ratio. Set to 1.0 to disable CPU overcommitment [16.0] : Enter the RAM overcommitment ratio. Set to 1.0 to disable RAM overcommitment [1.5] : Enter protocol which will be used for instance migration [tcp|ssh] [tcp] : Enter the compute manager for nova migration [nova.compute.manager.ComputeManager] : Enter the path to a PEM encoded certificate to be used on the https server, leave blank if one should be generated, this certificate should not require a passphrase: Enter the SSL keyfile corresponding to the certificate if one was entered: Enter the password for Neutron Keystone access : Confirm password : Enter the password for Neutron DB access : Confirm password : Enter the ovs bridge the Neutron L3 agent will use for external traffic, or 'provider' if using provider networks. [br-ex] : Enter Neutron metadata agent password : Confirm password : Should Packstack install Neutron LBaaS [y|n] [n] : Should Packstack install Neutron L3 Metering agent [y|n] [n] : Would you like to configure neutron FWaaS? [y|n] [n] : Would you like to configure neutron VPNaaS? [y|n] [n] : Enter a comma separated list of network type driver entrypoints [local|flat|vlan|gre|vxlan] [vxlan] : Enter a comma separated ordered list of network_types to allocate as tenant networks [local|vlan|gre|vxlan] [vxlan] : Enter a comma separated ordered list of networking mechanism driver entrypoints [logger|test|linuxbridge|openvswitch|hyperv|ncs|arista|cisco_nexus|mlnx|l2population] [openvswitch] : Enter a comma separated list of physical_network names with which flat networks can be created [*] : Enter a comma separated list of physical_network names usable for VLAN: Enter a comma separated list of <tun_min>:<tun_max> tuples enumerating ranges of GRE tunnel IDs that are available for tenant network allocation: Enter a multicast group for VXLAN: Enter a comma separated list of <vni_min>:<vni_max> tuples enumerating ranges of VXLAN VNI IDs that are available for tenant network allocation [10:100] : Enter the name of the L2 agent to be used with Neutron [linuxbridge|openvswitch] [openvswitch] : Enter a comma separated list of bridge mappings for the Neutron openvswitch plugin: Enter a comma separated list of OVS bridge:interface pairs for the Neutron openvswitch plugin: Enter interface with IP to override the default tunnel local_ip: Enter VXLAN UDP port number [4789] : Would you like to set up Horizon communication over https [y|n] [n] : Would you like to provision for demo usage and testing [y|n] [y] : n Would you like to configure Tempest (OpenStack test suite). Note that provisioning is only supported for all-in-one installations. [y|n] [n] : Would you like to configure the external ovs bridge [y|n] [n] : Enter the password for the Ceilometer Keystone access : Confirm password : Enter the IP address of the MongoDB server [192.168.129.130] : Enter the IP address of the redis master server [192.168.129.130] : Enter the port of the redis server(s) [6379] : Should redis try to use HA? [y|n] [n] : Enter the IP addresses of the redis slave servers: Enter the IP addresses of the redis sentinel servers: Enter the IP address of the coordination redis sentinel: Enter the port on which the redis sentinel servers listen [26379] : Enter the quorum value for the redis sentinel servers [2] : Enter the logical name of the master server [mymaster] : Packstack will be installed using the following configuration: ============================================================== ssh-public-key: /root/.ssh/id_rsa.pub default-password: mariadb-install: y os-glance-install: y os-cinder-install: y os-manila-install: n os-nova-install: y os-neutron-install: y os-horizon-install: y os-swift-install: n os-ceilometer-install: y os-heat-install: n os-sahara-install: n os-trove-install: n os-ironic-install: n os-client-install: y ntp-servers: nagios-install: n exclude-servers: os-debug-mode: n os-controller-host: 192.168.129.130 os-compute-hosts: 192.168.129.130 os-network-hosts: 192.168.129.130 os-vmware: n unsupported: n use-subnets: n use-epel: n additional-repo: enable-rdo-testing: n rh-username: rhn-satellite-server: ssl-cacert-file: /etc/pki/tls/certs/selfcert.crt ssl-cacert-key-file: /etc/pki/tls/private/selfkey.key ssl-cert-dir: ~/packstackca/ ssl-cacert-selfsign: y selfsign-cacert-subject-country:-- selfsign-cacert-subject-state: State selfsign-cacert-subject-location:City selfsign-cacert-subject-organization:openstack selfsign-cacert-subject-organizational-unit:packstack selfsign-cacert-subject-common-name:localhost.localdomain selfsign-cacert-subject-email: admin@localhost.localdomain amqp-backend: rabbitmq amqp-host: 192.168.129.130 amqp-enable-ssl: n amqp-enable-auth: n mariadb-host: 192.168.129.130 mariadb-pw: ******** keystone-db-passwd: ******** keystone-db-purge-enable: True keystone-region: RegionOne keystone-admin-email: root@localhost keystone-admin-username: admin keystone-admin-passwd: ******** keystone-demo-passwd: ******** keystone-service-name: keystone keystone-identity-backend: sql glance-db-passwd: ******** glance-ks-passwd: ******** glance-backend: file cinder-db-passwd: ******** cinder-db-purge-enable: True cinder-ks-passwd: ******** cinder-backend: lvm cinder-volumes-create: n cinder-volumes-size: 20G nova-db-purge-enable: True nova-db-passwd: ******** nova-ks-passwd: ******** novasched-cpu-allocation-ratio:16.0 novasched-ram-allocation-ratio:1.5 novacompute-migrate-protocol: tcp nova-compute-manager: nova.compute.manager.ComputeManager nova-ssl-cert: nova-ssl-key: os-neutron-ks-password: ******** os-neutron-db-password: ******** os-neutron-l3-ext-bridge: br-ex os-neutron-metadata-pw: ******** os-neutron-lbaas-install: n os-neutron-metering-agent-install:n neutron-fwaas: n os-neutron-vpnaas-install: n os-neutron-ml2-type-drivers: vxlan os-neutron-ml2-tenant-network-types:vxlan os-neutron-ml2-mechanism-drivers:openvswitch os-neutron-ml2-flat-networks: * os-neutron-ml2-vlan-ranges: os-neutron-ml2-tunnel-id-ranges: os-neutron-ml2-vxlan-group: os-neutron-ml2-vni-ranges: 10:100 os-neutron-l2-agent: openvswitch os-neutron-ovs-bridge-mappings: os-neutron-ovs-bridge-interfaces: os-neutron-ovs-tunnel-if: os-neutron-ovs-vxlan-udp-port: 4789 os-horizon-ssl: n provision-demo: n provision-tempest: n provision-tempest-repo-uri: https://github.com/openstack/tempest.git provision-tempest-repo-revision:master provision-all-in-one-ovs-bridge:n ceilometer-ks-passwd: ******** mongodb-host: 192.168.129.130 redis-master-host: 192.168.129.130 redis-port: 6379 redis-ha: n redis-slaves: redis-sentinels: redis-sentinel-contact: redis-sentinel-port: 26379 redis-sentinel-quorum: 2 redis-sentinel-master-name: mymaster Proceed with the configuration listed above? (yes|no): yes Installing: Clean Up [ DONE ] Discovering ip protocol version [ DONE ] Setting up ssh keys [ DONE ] Preparing servers [ DONE ] Pre installing Puppet and discovering hosts' details [ DONE ] Adding pre install manifest entries [ DONE ] Setting up CACERT [ DONE ] Adding AMQP manifest entries [ DONE ] Adding MariaDB manifest entries [ DONE ] Fixing Keystone LDAP config parameters to be undef if empty[ DONE ] Adding Keystone manifest entries [ DONE ] Adding Glance Keystone manifest entries [ DONE ] Adding Glance manifest entries [ DONE ] Adding Cinder Keystone manifest entries [ DONE ] Checking if the Cinder server has a cinder-volumes vg[ DONE ] Adding Cinder manifest entries [ DONE ] Adding Nova API manifest entries [ DONE ] Adding Nova Keystone manifest entries [ DONE ] Adding Nova Cert manifest entries [ DONE ] Adding Nova Conductor manifest entries [ DONE ] Creating ssh keys for Nova migration [ DONE ] Gathering ssh host keys for Nova migration [ DONE ] Adding Nova Compute manifest entries [ DONE ] Adding Nova Scheduler manifest entries [ DONE ] Adding Nova VNC Proxy manifest entries [ DONE ] Adding OpenStack Network-related Nova manifest entries[ DONE ] Adding Nova Common manifest entries [ DONE ] Adding Neutron VPNaaS Agent manifest entries [ DONE ] Adding Neutron FWaaS Agent manifest entries [ DONE ] Adding Neutron LBaaS Agent manifest entries [ DONE ] Adding Neutron API manifest entries [ DONE ] Adding Neutron Keystone manifest entries [ DONE ] Adding Neutron L3 manifest entries [ DONE ] Adding Neutron L2 Agent manifest entries [ DONE ] Adding Neutron DHCP Agent manifest entries [ DONE ] Adding Neutron Metering Agent manifest entries [ DONE ] Adding Neutron Metadata Agent manifest entries [ DONE ] Checking if NetworkManager is enabled and running [ DONE ] Adding OpenStack Client manifest entries [ DONE ] Adding Horizon manifest entries [ DONE ] Adding MongoDB manifest entries [ DONE ] Adding Redis manifest entries [ DONE ] Adding Ceilometer manifest entries [ DONE ] Adding Ceilometer Keystone manifest entries [ DONE ] Adding post install manifest entries [ DONE ] Copying Puppet modules and manifests [ DONE ] Applying 192.168.129.130_prescript.pp 192.168.129.130_prescript.pp: [ DONE ] Applying 192.168.129.130_amqp.pp Applying 192.168.129.130_mariadb.pp 192.168.129.130_amqp.pp: [ DONE ] 192.168.129.130_mariadb.pp: [ DONE ] Applying 192.168.129.130_keystone.pp Applying 192.168.129.130_glance.pp Applying 192.168.129.130_cinder.pp 192.168.129.130_keystone.pp: [ DONE ] 192.168.129.130_cinder.pp: [ DONE ] 192.168.129.130_glance.pp: [ DONE ] Applying 192.168.129.130_api_nova.pp 192.168.129.130_api_nova.pp: [ DONE ] Applying 192.168.129.130_nova.pp 192.168.129.130_nova.pp: [ DONE ] Applying 192.168.129.130_neutron.pp 192.168.129.130_neutron.pp: [ DONE ] Applying 192.168.129.130_osclient.pp Applying 192.168.129.130_horizon.pp 192.168.129.130_osclient.pp: [ DONE ] 192.168.129.130_horizon.pp: [ DONE ] Applying 192.168.129.130_mongodb.pp Applying 192.168.129.130_redis.pp 192.168.129.130_mongodb.pp: [ DONE ] 192.168.129.130_redis.pp: [ DONE ] Applying 192.168.129.130_ceilometer.pp 192.168.129.130_ceilometer.pp: [ DONE ] Applying 192.168.129.130_postscript.pp 192.168.129.130_postscript.pp: [ DONE ] Applying Puppet manifests [ DONE ] Finalizing [ DONE ] **** Installation completed successfully ****** Additional information: * A new answerfile was created in: /root/packstack-answers-20160420-021119.txt * Time synchronization installation was skipped. Please note that unsynchronized time on server instances might be problem for some OpenStack components. * Warning: NetworkManager is active on 192.168.129.130. OpenStack networking currently does not work on systems that have the Network Manager service enabled. * File /root/keystonerc_admin has been created on OpenStack client host 192.168.129.130. To use the command line tools you need to source the file. * To access the OpenStack Dashboard browse to http://192.168.129.130/dashboard . Please, find your login credentials stored in the keystonerc_admin in your home directory. * Because of the kernel update the host 192.168.129.130 requires reboot. * The installation log file is available at: /var/tmp/packstack/20160420-020826-gfsofC/openstack-setup.log * The generated manifests are available at: /var/tmp/packstack/20160420-020826-gfsofC/manifests [root@localhost yum.repos.d]#
标签:
原文地址:http://blog.csdn.net/xiangpingli/article/details/51239832