Add getting_started tutorial for Gophercloud SDK

Add getting_started tutorial for Gophercloud SDK, code had been tested
on a openstack cloud with neutron.

Change-Id: Ife8bc23671ddff175a5ff424e08893c746d97482
This commit is contained in:
Chung Chih, Hung 2016-02-17 11:24:54 +08:00
parent ed11113c62
commit 7f6e20ae5c
4 changed files with 489 additions and 1 deletions

View File

@ -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)
}

View File

@ -96,6 +96,11 @@ and toolkits with the OpenStack cloud:
- A .NET-based library. - A .NET-based library.
Use it to write C++ or C# code for Microsoft applications. Use it to write C++ or C# code for Microsoft applications.
- https://www.nuget.org/packages/openstack.net - https://www.nuget.org/packages/openstack.net
* - Go
- `gophercloud <https://github.com/rackspace/gophercloud>`_
- A go-based SDK.
Use it with multiple clouds.
- http://gophercloud.io/
For a list of available SDKs, see `Software Development Kits <https://wiki.openstack.org/wiki/SDKs>`_. For a list of available SDKs, see `Software Development Kits <https://wiki.openstack.org/wiki/SDKs>`_.
@ -181,6 +186,11 @@ To interact with the cloud, you must also have
.. note:: Before proceeding, install the latest version of shade. .. note:: Before proceeding, install the latest version of shade.
.. only:: gophercloud
`a recent version of gophercloud installed <https://godoc.org/github.com/rackspace/gophercloud>`_
Obtain the following information from your cloud provider: Obtain the following information from your cloud provider:
* auth URL * auth URL
@ -307,6 +317,18 @@ to run code snippets in your language of choice.
of the following API calls please double-check your of the following API calls please double-check your
credentials. 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 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) 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: 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. 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) 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: Next, tell the script which flavor you want to use:
.. only:: fog .. 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) 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. Now, you can launch the instance.
Launch an instance Launch an instance
@ -836,6 +914,19 @@ Create the instance.
:start-after: step-6 :start-after: step-6
:end-before: step-7 :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: If you list existing instances:
.. only:: fog .. only:: fog
@ -878,6 +969,13 @@ If you list existing instances:
:start-after: step-7 :start-after: step-7
:end-before: step-8 :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. The new instance appears.
.. only:: libcloud .. only:: libcloud
@ -982,6 +1080,14 @@ The new instance appears.
updated: '2015-07-20T20:31:10Z' updated: '2015-07-20T20:31:10Z'
user_id: bfd3dbf1c8a242cd90884408de547bb9 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. Before you continue, you must do one more thing.
Destroy an instance Destroy an instance
@ -1029,6 +1135,13 @@ cost money. To avoid unexpected expenses, destroy cloud resources.
:start-after: step-8 :start-after: step-8
:end-before: step-9 :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. If you list the instances again, the instance disappears.
Leave your shell open to use it for another instance deployment in this 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) 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 * 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 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`. 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 :start-after: step-10
:end-before: step-11 :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 * Userdata. During instance creation, you can provide userdata to OpenStack to
configure instances after they boot. The cloud-init service applies the 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 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 :start-after: step-11
:end-before: step-12 :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. Now, you can boot and configure the instance.
Boot and configure an instance Boot and configure an instance
@ -1215,6 +1349,13 @@ After you request the instance, wait for it to build.
:start-after: step-12 :start-after: step-12
:end-before: step-13 :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 When the instance boots, the `ex_userdata` variable value instructs the
instance to deploy the Fractals application. instance to deploy the Fractals application.
@ -1339,6 +1480,45 @@ instance.
:start-after: step-14 :start-after: step-14
:end-before: step-15 :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. Run the script to start the deployment.
@ -1376,6 +1556,12 @@ interface at the following link.
.. literalinclude:: ../samples/openstacksdk/getting_started.py .. literalinclude:: ../samples/openstacksdk/getting_started.py
:start-after: step-15 :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 .. note:: If you do not use floating IP addresses, substitute another IP
address, as appropriate. address, as appropriate.
@ -1442,3 +1628,8 @@ information, the flavor ID, and image ID.
.. literalinclude:: ../samples/openstacksdk/getting_started.py .. literalinclude:: ../samples/openstacksdk/getting_started.py
:language: python :language: python
.. only:: gophercloud
.. literalinclude:: ../samples/gophercloud/getting_started.go
:language: go

View File

@ -9,7 +9,7 @@ for tag in libcloud; do
done done
# Draft documents # 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 \ tools/build-rst.sh firstapp \
--tag ${tag} --target "api-ref/draft/firstapp-${tag}" --tag ${tag} --target "api-ref/draft/firstapp-${tag}"
done done

View File

@ -161,6 +161,9 @@ commands = sphinx-build -E -W -t libcloud firstapp/source firstapp/build/html
[testenv:firstapp-shade] [testenv:firstapp-shade]
commands = sphinx-build -E -W -t shade firstapp/source firstapp/build-shade/html 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] [testenv:api-quick-start]
commands = commands =
{toxinidir}/tools/build-api-quick-start.sh {toxinidir}/tools/build-api-quick-start.sh