diff --git a/firstapp/samples/gophercloud/getting_started.go b/firstapp/samples/gophercloud/getting_started.go new file mode 100644 index 000000000..6dcf30fdf --- /dev/null +++ b/firstapp/samples/gophercloud/getting_started.go @@ -0,0 +1,294 @@ +package main + +import ( + "fmt" + "io/ioutil" + + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" + "github.com/rackspace/gophercloud/openstack/compute/v2/flavors" + "github.com/rackspace/gophercloud/openstack/compute/v2/images" + "github.com/rackspace/gophercloud/openstack/compute/v2/servers" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" + "github.com/rackspace/gophercloud/openstack/networking/v2/networks" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external" +) + +func main() { + // step-1 + var authUsername string = "your_auth_username" + var authPassword string = "your_auth_password" + var authUrl string = "http://controller:5000" + var projectName string = "your_project_id" + var regionName string = "your_region_name" + + authOpts := gophercloud.AuthOptions{ + IdentityEndpoint: authUrl, + Username: authUsername, + Password: authPassword, + TenantID: projectName, + } + provider, _ := openstack.AuthenticatedClient(authOpts) + client, _ := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ + Region: regionName, + }) + + // step-2 + pager := images.ListDetail(client, images.ListOpts{}) + page, _ := pager.AllPages() + imageList, _ := images.ExtractImages(page) + fmt.Println(imageList) + + // step-3 + pager = flavors.ListDetail(client, flavors.ListOpts{}) + page, _ = pager.AllPages() + flavorList, _ := flavors.ExtractFlavors(page) + fmt.Println(flavorList) + + // step-4 + imageID := "74e6d1ec-9a08-444c-8518-4f232446386d" + image, _ := images.Get(client, imageID).Extract() + fmt.Println(image) + + // step-5 + flavorID := "1" + flavor, _ := flavors.Get(client, flavorID).Extract() + fmt.Println(flavor) + + // step-6 + instanceName := "testing" + testingInstance, _ := servers.Create(client, servers.CreateOpts{ + Name: instanceName, + ImageRef: imageID, + FlavorRef: flavorID, + }).Extract() + fmt.Println(testingInstance) + + // step-7 + pager = servers.List(client, servers.ListOpts{}) + page, _ = pager.AllPages() + serverList, _ := servers.ExtractServers(page) + fmt.Println(serverList) + + // step-8 + servers.Delete(client, testingInstance.ID) + + // step-9 + fmt.Println("Checking for existing SSH key pair...") + keyPairName := "demokey" + pubKeyFile := "~/.ssh/id_rsa.pub" + keyPairExists := false + + pager = keypairs.List(client) + page, _ = pager.AllPages() + keypairList, _ := keypairs.ExtractKeyPairs(page) + for _, k := range keypairList { + if k.Name == keyPairName { + keyPairExists = true + break + } + } + + if keyPairExists { + fmt.Println("Keypair " + keyPairName + " already exists. Skipping import.") + } else { + fmt.Println("adding keypair...") + bs, _ := ioutil.ReadFile(pubKeyFile) + keypairs.Create(client, keypairs.CreateOpts{ + Name: keyPairName, + PublicKey: string(bs), + }).Extract() + } + + pager = keypairs.List(client) + page, _ = pager.AllPages() + keypairList, _ = keypairs.ExtractKeyPairs(page) + fmt.Println(keypairList) + + // step-10 + fmt.Println("Checking for existing security group...") + var allInOneSecurityGroup secgroups.SecurityGroup + securityGroupName := "all-in-one" + securityGroupExists := false + + pager = secgroups.List(client) + page, _ = pager.AllPages() + secgroupList, _ := secgroups.ExtractSecurityGroups(page) + for _, secGroup := range secgroupList { + if secGroup.Name == securityGroupName { + allInOneSecurityGroup = secGroup + securityGroupExists = true + break + } + } + + if securityGroupExists { + fmt.Println("Security Group " + allInOneSecurityGroup.Name + " already exists. Skipping creation.") + } else { + allInOneSecurityGroup, _ := secgroups.Create(client, secgroups.CreateOpts{ + Name: securityGroupName, + Description: "network access for all-in-one application.", + }).Extract() + secgroups.CreateRule(client, secgroups.CreateRuleOpts{ + ParentGroupID: allInOneSecurityGroup.ID, + FromPort: 80, + ToPort: 80, + IPProtocol: "TCP", + CIDR: "0.0.0.0/0", + }).Extract() + secgroups.CreateRule(client, secgroups.CreateRuleOpts{ + ParentGroupID: allInOneSecurityGroup.ID, + FromPort: 22, + ToPort: 22, + IPProtocol: "TCP", + CIDR: "0.0.0.0/0", + }).Extract() + } + + pager = secgroups.List(client) + page, _ = pager.AllPages() + secgroupList, _ = secgroups.ExtractSecurityGroups(page) + fmt.Println(secgroupList) + + // step-11 + userData := `#!/usr/bin/env bash +curl -L -s https://git.openstack.org/cgit/openstack/faafo/plain/contrib/install.sh | bash -s -- \ + -i faafo -i messaging -r api -r worker -r demo +` + + // step-12 + fmt.Println("Checking for existing instance...") + instanceName = "all-in-one" + instanceExists := false + + pager = servers.List(client, servers.ListOpts{}) + page, _ = pager.AllPages() + serverList, _ = servers.ExtractServers(page) + for _, s := range serverList { + if s.Name == instanceName { + testingInstance = &s + instanceExists = true + break + } + } + + if instanceExists { + fmt.Println("Instance " + testingInstance.Name + " already exists. Skipping creation.") + } else { + opts := servers.CreateOpts{ + Name: instanceName, + ImageRef: image.ID, + FlavorRef: flavor.ID, + SecurityGroups: []string{securityGroupName}, + UserData: []byte(userData), + } + testingInstance, _ = servers.Create(client, keypairs.CreateOptsExt{ + CreateOptsBuilder: opts, + KeyName: keyPairName, + }).Extract() + } + servers.WaitForStatus(client, testingInstance.ID, "ACTIVE", 300) + + pager = servers.List(client, servers.ListOpts{}) + page, _ = pager.AllPages() + serverList, _ = servers.ExtractServers(page) + fmt.Println(serverList) + + // step-13 + var privateIP string + for t, addrs := range testingInstance.Addresses { + if t != "private" || len(privateIP) != 0 { + continue + } + addrs, ok := addrs.([]interface{}) + if !ok { + continue + } + for _, addr := range addrs { + a, ok := addr.(map[string]interface{}) + if !ok || a["version"].(float64) != 4 { + continue + } + ip, ok := a["addr"].(string) + if ok && len(ip) != 0 { + privateIP = ip + fmt.Println("Private IP found: " + privateIP) + break + } + } + } + + // step-14 + var publicIP string + for t, addrs := range testingInstance.Addresses { + if t != "public" || len(publicIP) != 0 { + continue + } + addrs, ok := addrs.([]interface{}) + if !ok { + continue + } + for _, addr := range addrs { + a, ok := addr.(map[string]interface{}) + if !ok || a["version"].(float64) != 4 { + continue + } + ip, ok := a["addr"].(string) + if ok && len(ip) != 0 { + publicIP = ip + fmt.Println("Public IP found: " + publicIP) + break + } + } + } + + // step-15 + fmt.Println("Checking for unused Floating IP...") + var unusedFloatingIP string + pager = floatingip.List(client) + page, _ = pager.AllPages() + floatingIPList, _ := floatingip.ExtractFloatingIPs(page) + for _, ip := range floatingIPList { + if ip.InstanceID == "" { + unusedFloatingIP = ip.IP + break + } + } + + networkClient, _ := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ + Region: regionName, + }) + + pager = networks.List(networkClient, networks.ListOpts{}) + page, _ = pager.AllPages() + poolList, _ := external.ExtractList(page) + for _, pool := range poolList { + if len(unusedFloatingIP) != 0 || !pool.External { + continue + } + fmt.Println("Allocating new Floating IP from pool: " + pool.Name) + f, _ := floatingip.Create(client, floatingip.CreateOpts{Pool: pool.Name}).Extract() + unusedFloatingIP = f.IP + } + + // step-16 + if len(publicIP) != 0 { + fmt.Println("Instance " + testingInstance.Name + " already has a public ip. Skipping attachment.") + } else { + floatingip.Associate(client, testingInstance.ID, unusedFloatingIP) + } + + // step-17 + var actualIPAddress string + if len(publicIP) != 0 { + actualIPAddress = publicIP + } else if len(unusedFloatingIP) != 0 { + actualIPAddress = unusedFloatingIP + } else { + actualIPAddress = privateIP + } + + fmt.Println("The Fractals app will be deployed to http://" + actualIPAddress) +} diff --git a/firstapp/source/getting_started.rst b/firstapp/source/getting_started.rst index d1a6dbe7e..daee9e9fb 100644 --- a/firstapp/source/getting_started.rst +++ b/firstapp/source/getting_started.rst @@ -96,6 +96,11 @@ and toolkits with the OpenStack cloud: - A .NET-based library. Use it to write C++ or C# code for Microsoft applications. - https://www.nuget.org/packages/openstack.net + * - Go + - `gophercloud `_ + - A go-based SDK. + Use it with multiple clouds. + - http://gophercloud.io/ For a list of available SDKs, see `Software Development Kits `_. @@ -181,6 +186,11 @@ To interact with the cloud, you must also have .. note:: Before proceeding, install the latest version of shade. +.. only:: gophercloud + + `a recent version of gophercloud installed `_ + + Obtain the following information from your cloud provider: * auth URL @@ -307,6 +317,18 @@ to run code snippets in your language of choice. of the following API calls please double-check your credentials. +.. only:: gophercloud + + To try it, add the following code to go file + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-1 + :end-before: step-2 + + .. note:: The client object accesses the Compute v2.0 service, + so that version is in this tutorial. + Flavors and images ~~~~~~~~~~~~~~~~~~ @@ -425,6 +447,20 @@ To list the images that are available in your cloud, run some API calls: openstack.image.v1.image.Image(attrs={u'name': u'ubuntu-14.04', u'container_format': u'bare', u'disk_format': u'qcow2', u'checksum': u'6d8f1c8cf05e1fbdc8b543fda1a9fa7f', u'id': u'cb6b7936-d2c5-4901-8678-c88b3a6ed84c', u'size': 258540032}, loaded=True) ... +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-2 + :end-before: step-3 + + This code returns output like this: + + .. code-block:: none + + [{74e6d1ec-9a08-444c-8518-4f232446386d 2016-02-01T07:20:31Z 0 0 cirros-0.3.4-x86_64-uec 100 ACTIVE 2016-02-01T07:20:32Z} + {f70b7fb0-348a-4519-b358-0f239dc64dc5 2016-02-01T07:20:30Z 0 0 cirros-0.3.4-x86_64-uec-ramdisk 100 ACTIVE 2016-02-01T07:20:31Z} + {e92f5e17-60d2-4cb5-b893-d605b136afab 2016-02-01T07:20:29Z 0 0 cirros-0.3.4-x86_64-uec-kernel 100 ACTIVE 2016-02-01T07:20:30Z}] You can also get information about available flavors: @@ -545,6 +581,22 @@ You can also get information about available flavors: ... +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-3 + :end-before: step-4 + + This code returns output like this: + + .. code-block:: none + + [{1 1 512 m1.tiny 1 0 1} + {2 20 2048 m1.small 1 0 1} + {3 40 4096 m1.medium 1 0 2} + ... + {84 0 128 m1.micro 1 0 1}] Your images and flavors will be different, of course. @@ -656,6 +708,19 @@ image that you picked in the previous section: openstack.image.v1.image.Image(attrs={u'name': u'ubuntu-14.04', u'container_format': u'bare', u'disk_format': u'qcow2', u'checksum': u'6d8f1c8cf05e1fbdc8b543fda1a9fa7f', u'id': u'cb6b7936-d2c5-4901-8678-c88b3a6ed84c', u'size': 258540032}, loaded=True) +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-4 + :end-before: step-5 + + You should see output like this: + + .. code-block:: none + + &{74e6d1ec-9a08-444c-8518-4f232446386d 2016-02-01T07:20:31Z 0 0 cirros-0.3.4-x86_64-uec 100 ACTIVE 2016-02-01T07:20:32Z} + Next, tell the script which flavor you want to use: .. only:: fog @@ -755,6 +820,19 @@ Next, tell the script which flavor you want to use: openstack.compute.v2.flavor.Flavor(attrs={u'name': u'm1.small', u'links': [{u'href': u'http://controller:8774/v2/96ff6aa79e60423d9848b70d5475c415/flavors/2', u'rel': u'self'}, {u'href': u'http://controller:8774/96ff6aa79e60423d9848b70d5475c415/flavors/2', u'rel': u'bookmark'}], u'ram': 2048, u'OS-FLV-DISABLED:disabled': False, u'vcpus': 1, u'swap': u'', u'os-flavor-access:is_public': True, u'rxtx_factor': 1.0, u'OS-FLV-EXT-DATA:ephemeral': 0, u'disk': 20, 'id': u'2'}, loaded=True) +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-5 + :end-before: step-6 + + You should see output like this: + + .. code-block:: none + + &{1 1 512 m1.tiny 1 0 1} + Now, you can launch the instance. Launch an instance @@ -836,6 +914,19 @@ Create the instance. :start-after: step-6 :end-before: step-7 +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-6 + :end-before: step-7 + + You should see output like this: + + .. code-block:: none + + &{739dd964-ae88-461d-9746-f8f1139d20f6 0 map[] map[] map[] map[] ... RPUkTFM8fynn [map[name:default]]} + If you list existing instances: .. only:: fog @@ -878,6 +969,13 @@ If you list existing instances: :start-after: step-7 :end-before: step-8 +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-7 + :end-before: step-8 + The new instance appears. .. only:: libcloud @@ -982,6 +1080,14 @@ The new instance appears. updated: '2015-07-20T20:31:10Z' user_id: bfd3dbf1c8a242cd90884408de547bb9 +.. only:: gophercloud + + .. code-block:: none + + [... + {739dd964-ae88-461d-9746-f8f1139d20f6 061fdb617b6c4bdf8694bf5b0d8eefdd bb210009e42c4b509ba75893a757c8e5 testing 2016-02-16T07:16:52Z 2016-02-16T07:16:52Z 2d2f4bba90498fd46c72e7d019dde9189c36637b73e71e1e652d75db BUILD 0 ... [map[name:default]]} + ...] + Before you continue, you must do one more thing. Destroy an instance @@ -1029,6 +1135,13 @@ cost money. To avoid unexpected expenses, destroy cloud resources. :start-after: step-8 :end-before: step-9 +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-8 + :end-before: step-9 + If you list the instances again, the instance disappears. Leave your shell open to use it for another instance deployment in this @@ -1101,6 +1214,13 @@ your public SSH key file. openstack.compute.v2.keypair.Keypair(attrs={u'public_key': u'ssh-rsa ABAAABAQCyyzkyaPf.....', u'name': u'demokey', u'fingerprint': aa:bb:cc:... '}, loaded=True) +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-9 + :end-before: step-10 + * Network access. By default, OpenStack filters all traffic. You must create a security group and apply it to your instance. The security group allows HTTP and SSH access. We will go into more detail in :doc:`/introduction`. @@ -1136,6 +1256,13 @@ your public SSH key file. :start-after: step-10 :end-before: step-11 +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-10 + :end-before: step-11 + * Userdata. During instance creation, you can provide userdata to OpenStack to configure instances after they boot. The cloud-init service applies the user data to an instance. You must pre-install the cloud-init service on your @@ -1174,6 +1301,13 @@ your public SSH key file. :start-after: step-11 :end-before: step-12 +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-11 + :end-before: step-12 + Now, you can boot and configure the instance. Boot and configure an instance @@ -1215,6 +1349,13 @@ After you request the instance, wait for it to build. :start-after: step-12 :end-before: step-13 +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-12 + :end-before: step-13 + When the instance boots, the `ex_userdata` variable value instructs the instance to deploy the Fractals application. @@ -1339,6 +1480,45 @@ instance. :start-after: step-14 :end-before: step-15 +.. only:: gophercloud + + To see whether a private IP address is assigned to your instance: + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-13 + :end-before: step-14 + + + + If one is assigned, users can use this address to access the instance on + some OpenStack clouds. + + To determine whether a public IP address is assigned to your instance: + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-14 + :end-before: step-15 + + If one is assigned, users can use this address to access the instance. + + To create a floating IP address to use with your instance: + + Use network service client to select the first floating IP address pool. + Allocate this pool to your project and use it to get a floating IP address. + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-15 + :end-before: step-16 + + Attach the floating IP address to the instance: + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-16 + :end-before: step-17 Run the script to start the deployment. @@ -1376,6 +1556,12 @@ interface at the following link. .. literalinclude:: ../samples/openstacksdk/getting_started.py :start-after: step-15 +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go + :start-after: step-17 + .. note:: If you do not use floating IP addresses, substitute another IP address, as appropriate. @@ -1442,3 +1628,8 @@ information, the flavor ID, and image ID. .. literalinclude:: ../samples/openstacksdk/getting_started.py :language: python + +.. only:: gophercloud + + .. literalinclude:: ../samples/gophercloud/getting_started.go + :language: go diff --git a/tools/build-firstapp-rst.sh b/tools/build-firstapp-rst.sh index ec13784d7..88b0575cb 100755 --- a/tools/build-firstapp-rst.sh +++ b/tools/build-firstapp-rst.sh @@ -9,7 +9,7 @@ for tag in libcloud; do done # Draft documents -for tag in dotnet fog openstacksdk pkgcloud shade jclouds; do +for tag in dotnet fog openstacksdk pkgcloud shade jclouds gophercloud; do tools/build-rst.sh firstapp \ --tag ${tag} --target "api-ref/draft/firstapp-${tag}" done diff --git a/tox.ini b/tox.ini index c2e103f01..ae40ea59b 100644 --- a/tox.ini +++ b/tox.ini @@ -161,6 +161,9 @@ commands = sphinx-build -E -W -t libcloud firstapp/source firstapp/build/html [testenv:firstapp-shade] commands = sphinx-build -E -W -t shade firstapp/source firstapp/build-shade/html +[testenv:firstapp-gophercloud] +commands = sphinx-build -E -W -t gophercloud firstapp/source firstapp/build-gophercloud/html + [testenv:api-quick-start] commands = {toxinidir}/tools/build-api-quick-start.sh