2014年9月30日星期二

解决PKIX:unable to find valid certification path to requested target 的问题

这两天在twitter服务器上忽然遇到这样的异常:
e: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 
经过检查确认,完整的异常信息应该如下:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
后来到twitter开发者网站上去看到了也有人遇到了类似的问题,链接如下:https://dev.twitter.com/discussions/533
但是都没说明具体的解决方法,郁闷啊!!
在oracle某一博客上说是可以这样解决的:http://blogs.oracle.com/gc/entry/unable_to_find_valid_certification
但是悲催的是那个java类已经无法下载了。。。。历经N次的google,终于找到该文件了,不敢独享,记录于此。

问题的根本是:
缺少安全证书时出现的异常。
解决问题方法:
将你要访问的webservice/url....的安全认证证书导入到客户端即可。

以下是获取安全证书的一种方法,通过以下程序获取安全证书:
[java] view plaincopy
  1. /* 
  2.  * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved. 
  3.  * 
  4.  * Redistribution and use in source and binary forms, with or without 
  5.  * modification, are permitted provided that the following conditions 
  6.  * are met: 
  7.  * 
  8.  *   - Redistributions of source code must retain the above copyright 
  9.  *     notice, this list of conditions and the following disclaimer. 
  10.  * 
  11.  *   - Redistributions in binary form must reproduce the above copyright 
  12.  *     notice, this list of conditions and the following disclaimer in the 
  13.  *     documentation and/or other materials provided with the distribution. 
  14.  * 
  15.  *   - Neither the name of Sun Microsystems nor the names of its 
  16.  *     contributors may be used to endorse or promote products derived 
  17.  *     from this software without specific prior written permission. 
  18.  * 
  19.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
  20.  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
  21.  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  22.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
  23.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
  24.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
  25.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  26.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  27.  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  28.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
  29.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  30.  */  
  31.   
  32. import java.io.BufferedReader;  
  33. import java.io.File;  
  34. import java.io.FileInputStream;  
  35. import java.io.FileOutputStream;  
  36. import java.io.InputStream;  
  37. import java.io.InputStreamReader;  
  38. import java.io.OutputStream;  
  39. import java.security.KeyStore;  
  40. import java.security.MessageDigest;  
  41. import java.security.cert.CertificateException;  
  42. import java.security.cert.X509Certificate;  
  43.   
  44. import javax.net.ssl.SSLContext;  
  45. import javax.net.ssl.SSLException;  
  46. import javax.net.ssl.SSLSocket;  
  47. import javax.net.ssl.SSLSocketFactory;  
  48. import javax.net.ssl.TrustManager;  
  49. import javax.net.ssl.TrustManagerFactory;  
  50. import javax.net.ssl.X509TrustManager;  
  51.   
  52. public class InstallCert {  
  53.   
  54.     public static void main(String[] args) throws Exception {  
  55.         String host;  
  56.         int port;  
  57.         char[] passphrase;  
  58.         if ((args.length == 1) || (args.length == 2)) {  
  59.             String[] c = args[0].split(":");  
  60.             host = c[0];  
  61.             port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);  
  62.             String p = (args.length == 1) ? "changeit" : args[1];  
  63.             passphrase = p.toCharArray();  
  64.         } else {  
  65.             System.out  
  66.                     .println("Usage: java InstallCert <host>[:port] [passphrase]");  
  67.             return;  
  68.         }  
  69.   
  70.         File file = new File("jssecacerts");  
  71.         if (file.isFile() == false) {  
  72.             char SEP = File.separatorChar;  
  73.             File dir = new File(System.getProperty("java.home") + SEP + "lib"  
  74.                     + SEP + "security");  
  75.             file = new File(dir, "jssecacerts");  
  76.             if (file.isFile() == false) {  
  77.                 file = new File(dir, "cacerts");  
  78.             }  
  79.         }  
  80.         System.out.println("Loading KeyStore " + file + "...");  
  81.         InputStream in = new FileInputStream(file);  
  82.         KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());  
  83.         ks.load(in, passphrase);  
  84.         in.close();  
  85.   
  86.         SSLContext context = SSLContext.getInstance("TLS");  
  87.         TrustManagerFactory tmf = TrustManagerFactory  
  88.                 .getInstance(TrustManagerFactory.getDefaultAlgorithm());  
  89.         tmf.init(ks);  
  90.         X509TrustManager defaultTrustManager = (X509TrustManager) tmf  
  91.                 .getTrustManagers()[0];  
  92.         SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);  
  93.         context.init(nullnew TrustManager[] { tm }, null);  
  94.         SSLSocketFactory factory = context.getSocketFactory();  
  95.   
  96.         System.out  
  97.                 .println("Opening connection to " + host + ":" + port + "...");  
  98.         SSLSocket socket = (SSLSocket) factory.createSocket(host, port);  
  99.         socket.setSoTimeout(10000);  
  100.         try {  
  101.             System.out.println("Starting SSL handshake...");  
  102.             socket.startHandshake();  
  103.             socket.close();  
  104.             System.out.println();  
  105.             System.out.println("No errors, certificate is already trusted");  
  106.         } catch (SSLException e) {  
  107.             System.out.println();  
  108.             e.printStackTrace(System.out);  
  109.         }  
  110.   
  111.         X509Certificate[] chain = tm.chain;  
  112.         if (chain == null) {  
  113.             System.out.println("Could not obtain server certificate chain");  
  114.             return;  
  115.         }  
  116.   
  117.         BufferedReader reader = new BufferedReader(new InputStreamReader(  
  118.                 System.in));  
  119.   
  120.         System.out.println();  
  121.         System.out.println("Server sent " + chain.length + " certificate(s):");  
  122.         System.out.println();  
  123.         MessageDigest sha1 = MessageDigest.getInstance("SHA1");  
  124.         MessageDigest md5 = MessageDigest.getInstance("MD5");  
  125.         for (int i = 0; i < chain.length; i++) {  
  126.             X509Certificate cert = chain[i];  
  127.             System.out.println(" " + (i + 1) + " Subject "  
  128.                     + cert.getSubjectDN());  
  129.             System.out.println("   Issuer  " + cert.getIssuerDN());  
  130.             sha1.update(cert.getEncoded());  
  131.             System.out.println("   sha1    " + toHexString(sha1.digest()));  
  132.             md5.update(cert.getEncoded());  
  133.             System.out.println("   md5     " + toHexString(md5.digest()));  
  134.             System.out.println();  
  135.         }  
  136.   
  137.         System.out  
  138.                 .println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");  
  139.         String line = reader.readLine().trim();  
  140.         int k;  
  141.         try {  
  142.             k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;  
  143.         } catch (NumberFormatException e) {  
  144.             System.out.println("KeyStore not changed");  
  145.             return;  
  146.         }  
  147.   
  148.         X509Certificate cert = chain[k];  
  149.         String alias = host + "-" + (k + 1);  
  150.         ks.setCertificateEntry(alias, cert);  
  151.   
  152.         OutputStream out = new FileOutputStream("jssecacerts");  
  153.         ks.store(out, passphrase);  
  154.         out.close();  
  155.   
  156.         System.out.println();  
  157.         System.out.println(cert);  
  158.         System.out.println();  
  159.         System.out  
  160.                 .println("Added certificate to keystore 'jssecacerts' using alias '"  
  161.                         + alias + "'");  
  162.     }  
  163.   
  164.     private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();  
  165.   
  166.     private static String toHexString(byte[] bytes) {  
  167.         StringBuilder sb = new StringBuilder(bytes.length * 3);  
  168.         for (int b : bytes) {  
  169.             b &= 0xff;  
  170.             sb.append(HEXDIGITS[b >> 4]);  
  171.             sb.append(HEXDIGITS[b & 15]);  
  172.             sb.append(' ');  
  173.         }  
  174.         return sb.toString();  
  175.     }  
  176.   
  177.     private static class SavingTrustManager implements X509TrustManager {  
  178.   
  179.         private final X509TrustManager tm;  
  180.         private X509Certificate[] chain;  
  181.   
  182.         SavingTrustManager(X509TrustManager tm) {  
  183.             this.tm = tm;  
  184.         }  
  185.   
  186.         public X509Certificate[] getAcceptedIssuers() {  
  187.             throw new UnsupportedOperationException();  
  188.         }  
  189.   
  190.         public void checkClientTrusted(X509Certificate[] chain, String authType)  
  191.                 throws CertificateException {  
  192.             throw new UnsupportedOperationException();  
  193.         }  
  194.   
  195.         public void checkServerTrusted(X509Certificate[] chain, String authType)  
  196.                 throws CertificateException {  
  197.             this.chain = chain;  
  198.             tm.checkServerTrusted(chain, authType);  
  199.         }  
  200.     }  
  201.   
  202. }  

编译InstallCert.java,然后执行:java InstallCert hostname,比如:
java InstallCert www.twitter.com
会看到如下信息:
[java] view plaincopy
  1. java InstallCert www.twitter.com  
  2. Loading KeyStore /usr/java/jdk1.6.0_16/jre/lib/security/cacerts...  
  3. Opening connection to www.twitter.com:443...  
  4. Starting SSL handshake...  
  5.   
  6. javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  
  7.     at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)  
  8.     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1476)  
  9.     at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:174)  
  10.     at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:168)  
  11.     at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:846)  
  12.     at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:106)  
  13.     at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:495)  
  14.     at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:433)  
  15.     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:815)  
  16.     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1025)  
  17.     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1038)  
  18.     at InstallCert.main(InstallCert.java:63)  
  19. Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  
  20.     at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:221)  
  21.     at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:145)  
  22.     at sun.security.validator.Validator.validate(Validator.java:203)  
  23.     at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)  
  24.     at InstallCert$SavingTrustManager.checkServerTrusted(InstallCert.java:158)  
  25.     at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)  
  26.     at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:839)  
  27.     ... 7 more  
  28. Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  
  29.     at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:236)  
  30.     at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:194)  
  31.     at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:216)  
  32.     ... 13 more  
  33.   
  34. Server sent 2 certificate(s):  
  35.   
  36.  1 Subject CN=www.twitter.com, O=example.com, C=US  
  37.    Issuer  CN=Certificate Shack, O=example.com, C=US  
  38.    sha1    2e 7f 76 9b 52 91 09 2e 5d 8f 6b 61 39 2d 5e 06 e4 d8 e9 c7   
  39.    md5     dd d1 a8 03 d7 6c 4b 11 a7 3d 74 28 89 d0 67 54   
  40.   
  41.  2 Subject CN=Certificate Shack, O=example.com, C=US  
  42.    Issuer  CN=Certificate Shack, O=example.com, C=US  
  43.    sha1    fb 58 a7 03 c4 4e 3b 0e e3 2c 40 2f 87 64 13 4d df e1 a1 a6   
  44.    md5     72 a0 95 43 7e 41 88 18 ae 2f 6d 98 01 2c 89 68   
  45.   
  46. Enter certificate to add to trusted keystore or 'q' to quit: [1]  

输入1,回车,然后会在当前的目录下产生一个名为“ssecacerts”的证书。
将证书拷贝到$JAVA_HOME/jre/lib/security目录下,或者通过以下方式:
System.setProperty("javax.net.ssl.trustStore", "你的jssecacerts证书路径");


注意:因为是静态加载,所以要重新启动你的Web Server,证书才能生效。

How to: Create an encrypted Asset and upload to storage

This article is one in a series introducing Azure Media Services programming. The previous topic was Setting Up Your Computer for Media Services.
To get media content into Media Services, first create an asset and add files to it, and then upload the asset. This process is called ingesting content.
When you create assets, you can specify three different options for encryption.
  • AssetCreationOptions.None: no encryption. If you want to create an unencrypted asset, you must set this option.
  • AssetCreationOptions.CommonEncryptionProtected: for Common Encryption Protected (CENC) files. An example is a set of files that are already PlayReady encrypted.
  • AssetCreationOptions.StorageEncrypted: storage encryption. Encrypts a clear input file before it is uploaded to Azure storage.
WACOM.NOTE Media Services provides on-disk storage encryption for your assets, not over-the-wire like Digital Rights Manager (DRM).
The sample code below does the following:
  • Creates an empty Asset.
  • Creates an AssetFile instance that we want to associate with the asset.
  • Creates an AccessPolicy instance that defines the permissions and duration of access to the asset.
  • Creates a Locator instance that provides access to the asset.
  • Uploads a single media file into Media Services.
static private IAsset CreateEmptyAsset(string assetName, AssetCreationOptions assetCreationOptions)
{
    var asset = _context.Assets.Create(assetName, assetCreationOptions);

    Console.WriteLine("Asset name: " + asset.Name);
    Console.WriteLine("Time created: " + asset.Created.Date.ToString());

    return asset;
}

static public IAsset CreateAssetAndUploadSingleFile(AssetCreationOptions assetCreationOptions, string singleFilePath)
{
    var assetName = "UploadSingleFile_" + DateTime.UtcNow.ToString();
    var asset = CreateEmptyAsset(assetName, assetCreationOptions);

    var fileName = Path.GetFileName(singleFilePath);

    var assetFile = asset.AssetFiles.Create(fileName);

    Console.WriteLine("Created assetFile {0}", assetFile.Name);
    Console.WriteLine("Upload {0}", assetFile.Name);

    assetFile.Upload(singleFilePath);
    Console.WriteLine("Done uploading of {0} using Upload()", assetFile.Name);

    return asset;
}
The following code shows how to create an asset and upload multiple files.
static public IAsset CreateAssetAndUploadMultipleFiles( AssetCreationOptions assetCreationOptions, string folderPath)
{
    var assetName = "UploadMultipleFiles_" + DateTime.UtcNow.ToString();

    var asset = CreateEmptyAsset(assetName, assetCreationOptions);

    var accessPolicy = _context.AccessPolicies.Create(assetName, TimeSpan.FromDays(30),
                                                        AccessPermissions.Write | AccessPermissions.List);
    var locator = _context.Locators.CreateLocator(LocatorType.Sas, asset, accessPolicy);

    var blobTransferClient = new BlobTransferClient();
    blobTransferClient.NumberOfConcurrentTransfers = 20;
    blobTransferClient.ParallelTransferThreadCount = 20;

    blobTransferClient.TransferProgressChanged += blobTransferClient_TransferProgressChanged;

    var filePaths = Directory.EnumerateFiles(folderPath);

    Console.WriteLine("There are {0} files in {1}", filePaths.Count(), folderPath);

    if (!filePaths.Any())
    {
        throw new FileNotFoundException(String.Format("No files in directory, check folderPath: {0}", folderPath));
    }

    var uploadTasks = new List<Task>();
    foreach (var filePath in filePaths)
    {
        var assetFile = asset.AssetFiles.Create(Path.GetFileName(filePath));
        Console.WriteLine("Created assetFile {0}", assetFile.Name);
                
        // It is recommended to validate AccestFiles before upload. 
        Console.WriteLine("Start uploading of {0}", assetFile.Name);
        uploadTasks.Add(assetFile.UploadAsync(filePath, blobTransferClient, locator, CancellationToken.None));
    }

    Task.WaitAll(uploadTasks.ToArray());
    Console.WriteLine("Done uploading the files");

    blobTransferClient.TransferProgressChanged -= blobTransferClient_TransferProgressChanged;

    locator.Delete();
    accessPolicy.Delete();

    return asset;
}

static void  blobTransferClient_TransferProgressChanged(object sender, BlobTransferProgressChangedEventArgs e)
{
    if (e.ProgressPercentage > 4) // Avoid startup jitter, as the upload tasks are added.
    {
        Console.WriteLine("{0}% upload competed for {1}.", e.ProgressPercentage, e.LocalFile);
    }
}

Setting up your computer for Media Services development

This section contains general prerequisites for Media Services development using the Media Services SDK for .NET. It also shows developers how to create a Visual Studio application for Media Services SDK development.

Prerequisites

  • A Media Services account in a new or existing Azure subscription. See the topic How to Create a Media Services Account.
  • Operating Systems: Windows 7, Windows 2008 R2, or Windows 8.
  • .NET Framework 4.
  • Visual Studio 2013, Visual Studio 2012, or Visual Studio 2010 SP1 (Professional, Premium, Ultimate, or Express).
  • Use the windowsazure.mediaservices Nuget package to install Azure SDK for .NET. The following section shows how to useNuget to install the Azure SDK.

To set up your Media Services account, use the Azure Management Portal (recommended). See the topic How to Create a Media Services Account. After creating your account in the Management Portal, you are ready to set up your computer for Media Services development.

Creating an Application in Visual Studio

This section shows you how to create a project in Visual Studio and set it up for Media Services development. In this case the project is a C# Windows console application, but the same setup steps shown here apply to other types of projects you can create for Media Services applications (for example, a Windows Forms application or an ASP.NET Web application).
  1. Create a new C# Console Application in Visual Studio 2013, Visual Studio 2012 or Visual Studio 2010 SP1. Enter theNameLocation, and Solution name, and then click OK.
  2. Make sure to set the target framework to .NET Framework 4. To do this, click the right mouse button on the Visual Studio project and select Properties. In the Application tab, set .NET Framework 4 for the target framework.
  3. Add a reference to System.Configuration assembly. To add a reference to System.Configuration, in Solution Explorer, right-click the References node and select Add Reference.... In the Manage References dialog, selectSystem.Configuration and click OK.
  4. Use the windowsazure.mediaservices Nuget package to add references to Azure SDK for .NET. (all other dependent assemblies will be installed as well).
    To add references using Nuget, do the following. In Visual Studio Main Menu, select TOOLS -> Library Package Manager-> Package Manager Console. In the console window type Install-Package windowsazure.mediaservices and press Enter.
  5. Overwrite the existing using statements at the beginning of the Program.cs file with the following code.
    using System;
    using System.Linq;
    using System.Configuration;
    using System.IO;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Collections.Generic;
    using Microsoft.WindowsAzure.MediaServices.Client;
At this point, you are ready to start developing a Media Services application.

Nearly everything you do in Media Services programming requires a reference to the server context object. The server context gives you programmatic access to all Media Services programming objects.
To get a reference to the server context, create a new instance of the context type as in the following code example. Pass your Media Services account name and account key (which you obtained during the account setup process) to the constructor.
// Create and cache the Media Services credentials in a static class variable.
_cachedCredentials = new MediaServicesCredentials(
                _mediaServicesAccountName,
                _mediaServicesAccountKey);

// Use the cached credentials to create CloudMediaContext.
_context = new CloudMediaContext(_cachedCredentials);
It is often useful to define a module-level variable of type CloudMediaContext to hold a reference to the server context. For more information, see Connecting to Media Services with the Media Services SDK for .NET.
The rest of the code examples in this topic use a variable called _context to refer to the server context.

How to Create a Media Services Account

  1. In the Management Portal, click New, click Media Service, and then click Quick Create.
    Media Services Quick Create
  2. In NAME, enter the name of the new account. A Media Services account name is all lower-case numbers or letters with no spaces, and is 3 - 24 characters in length.
  3. In REGION, select the geographic region that will be used to store the metadata records for your Media Services account. Only the available Media Services regions appear in the dropdown.
  4. In STORAGE ACCOUNT, select a storage account to provide blob storage of the media content from your Media Services account. You can select an existing storage account in the same geographic region as your Media Services account, or you can create a new storage account. A new storage account is created in the same region.
  5. If you created a new storage account, in NEW STORAGE ACCOUNT NAME, enter a name for the storage account. The rules for storage account names are the same as for Media Services accounts.
  6. Click Quick Create at the bottom of the form.
    You can monitor the status of the process in the message area at the bottom of the window.
    The media services page opens with the new account displayed. When the status changes to Active, it means the account is successfully created.
    Media Services Page
    When you double-click on the account name, the Quick Start page is displayed by default. This page enables you to do some management tasks that are also available on other pages of the portal. For example, you can upload a video file from this page, or do it from the CONTENT page.
    In addition, you can view code that uses Azure Media Services SDK to accomplish the following tasks: upload, encode, and publish videos. You can click on one of the links under WRITE SOME CODE section, copy the code and use it in your application.

windows azure测试1

1,测试windows azure虚机更改配置后,DNS ip是否会改变?
答:我在测试时,没有改变,有个可能,但是它有改变的风险(会改变)。
2,测试windows azure虚机更改配置后,D盘数据是否会丢失?
答:我在测试时,D盘的数据会丢失。

所以在更新虚机配置时,要注意两点:
1,先备份D盘的数据
2,在同一个云服务下,先新建一台虚机,并运行,保证在要改变配置的虚机不释放DNS IP。
3,在更新配置后,可以根据情况再决定是否删除新建的虚机。

每日英语

Nothing is so common as the wish to be remarkable.
没有什么比希望不平凡而更平凡的了。

2014年9月29日星期一

每日英语

Friend is who can give you strength at last.
朋友是在最后可以给你力量的人。—《当哈利遇见莎莉》