diff --git a/firstapp/samples/openstacksdk/getting_started.py b/firstapp/samples/openstacksdk/getting_started.py new file mode 100644 index 000000000..36e582bd1 --- /dev/null +++ b/firstapp/samples/openstacksdk/getting_started.py @@ -0,0 +1,188 @@ +# step-1 +import base64 +from os.path import expanduser + +from openstack import connection +from openstack import exceptions + +auth_username = 'your_auth_username' +auth_password = 'your_auth_password' +auth_url = 'http://controller:5000/v2.0' +project_name = 'your_project_name_or_id' +region_name = 'your_region_name' + +conn = connection.Connection(auth_url=auth_url, + project_name=project_name, + username=auth_username, + password=auth_password, + region=region_name) + +# step-2 +images = conn.image.images() +for image in images: + print(image) + +# step-3 +flavors = conn.compute.flavors() +for flavor in flavors: + print(flavor) + +# step-4 +image_id = 'cb6b7936-d2c5-4901-8678-c88b3a6ed84c' +image = conn.compute.get_image(image_id) +print(image) + +# step-5 +flavor_id = '2' +flavor = conn.compute.get_flavor(flavor_id) +print(flavor) + +# step-6 +instance_name = 'testing' +image_args = { + 'name': instance_name, + 'imageRef': image, + 'flavorRef': flavor +} + +testing_instance = conn.compute.create_server(**image_args) +print(testing_instance) + +# step-7 +instances = conn.compute.servers() +for instance in instances: + print(instance) + +# step-8 +conn.compute.delete_server(testing_instance) + +# step-9 +print('Checking for existing SSH key pair...') +keypair_name = 'demokey' +keypair_exists = False +for keypair in conn.compute.keypairs(): + if keypair.name == keypair_name: + keypair_exists = True + +if keypair_exists: + print('Keypair ' + keypair_name + ' already exists. Skipping import.') +else: + print('adding keypair...') + pub_key_file = open(expanduser('~/.ssh/id_rsa.pub')).read() + keypair_args = { + "name": keypair_name, + "public_key": pub_key_file + } + conn.compute.create_keypair(**keypair_args) + +for keypair in conn.compute.keypairs(): + print(keypair) + +# step-10 +print('Checking for existing security group...') +security_group_name = 'all-in-one' +security_group_exists = False +for security_group in conn.network.security_groups(): + if security_group.name == security_group_name: + all_in_one_security_group = security_group + security_group_exists = True + +if security_group_exists: + print('Security Group ' + all_in_one_security_group.name + ' already exists. Skipping creation.') +else: + security_group_args = { + 'name' : security_group_name, + 'description': 'network access for all-in-one application.' + } + all_in_one_security_group = conn.network.create_security_group(**security_group_args) + + security_rule_args = { + 'security_group_id': all_in_one_security_group, + 'direction': 'ingress', + 'protocol': 'tcp', + 'port_range_min': '80', + 'port_range_max': '80' + } + conn.network.create_security_group_rule(**security_rule_args) + + security_rule_args['port_range_min'] = '22' + security_rule_args['port_range_max'] = '22' + conn.network.create_security_group_rule(**security_rule_args) + +for security_group in conn.network.security_groups(): + print(security_group) + +# 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 +''' +userdata_b64str = base64.b64encode(userdata) + +# step-12 +print('Checking for existing instance...') +instance_name = 'all-in-one' +instance_exists = False +for instance in conn.compute.servers(): + if instance.name == instance_name: + testing_instance = instance + instance_exists = True + +if instance_exists: + print('Instance ' + testing_instance.name + ' already exists. Skipping creation.') +else: + + testing_instance_args = { + 'name': instance_name, + 'imageRef': image, + 'flavorRef': flavor, + 'key_name': keypair_name, + 'user_data': userdata_b64str, + 'security_groups': [{'name': all_in_one_security_group.name}] + } + + testing_instance = conn.compute.create_server(**testing_instance_args) + conn.compute.wait_for_server(testing_instance) + +for instance in conn.compute.servers(): + print(instance) + +# step-13 +print('Checking if Floating IP is already assigned to testing_instance...') +testing_instance_floating_ip = None +for values in testing_instance.addresses.itervalues(): + for address in values: + if address['OS-EXT-IPS:type'] == 'floating': + testing_instance_floating_ip = conn.network.find_ip(address['addr']) + +unused_floating_ip = None +if not testing_instance_floating_ip: + print('Checking for unused Floating IP...') + for floating_ip in conn.network.ips(): + if not floating_ip.fixed_ip_address: + unused_floating_ip = floating_ip + break + +if not testing_instance_floating_ip and not unused_floating_ip: + print('No free unused Floating IPs. Allocating new Floating IP...') + public_network_id = conn.network.find_network('public').id + try: + unused_floating_ip = conn.network.create_ip(floating_network_id=public_network_id) + unused_floating_ip = conn.network.get_ip(unused_floating_ip) + print(unused_floating_ip) + except exceptions.HttpException as e: + print(e) + +# step-14 +if testing_instance_floating_ip: + print('Instance ' + testing_instance.name + ' already has a public ip. Skipping attachment.') +else: + for port in conn.network.ports(): + if port.device_id == testing_instance.id: + testing_instance_port = port + + testing_instance_floating_ip = unused_floating_ip + conn.network.add_ip_to_port(testing_instance_port, testing_instance_floating_ip) + +# step-15 +print('The Fractals app will be deployed to http://%s' % testing_instance_floating_ip.floating_ip_address) diff --git a/firstapp/source/getting_started.rst b/firstapp/source/getting_started.rst index a80145f28..26929df96 100644 --- a/firstapp/source/getting_started.rst +++ b/firstapp/source/getting_started.rst @@ -123,8 +123,8 @@ To interact with the cloud, you must also have .. only:: libcloud - `libcloud 0.15.1 or higher installed - `_. + `libcloud 0.15.1 or higher installed + `_. .. only:: pkgcloud @@ -135,11 +135,8 @@ To interact with the cloud, you must also have .. only:: openstacksdk - the OpenStack SDK installed. - - .. warning:: - - This document has not yet been completed for the OpenStack SDK. + a recent version of `openstacksdk `_ + installed. .. only:: phpopencloud @@ -204,12 +201,12 @@ to run code snippets in your language of choice. .. only:: openstacksdk - .. code-block:: python + To try it out, add the following code to a Python script (or use an + interactive Python shell) by calling :code:`python -i`. - from openstack import connection - conn = connection.Connection(auth_url="http://controller:5000/v3", - user_name="your_auth_username", - password="your_auth_password", ...) + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-1 + :end-before: step-2 .. only:: pkgcloud @@ -273,6 +270,14 @@ to run code snippets in your language of choice. :start-after: step-1 :end-before: step-2 +.. only:: openstacksdk + + .. note:: If you receive the exception + :code:`openstack.exceptions.HttpException: HttpException: + 401 Client Error: Unauthorized,` while trying to run one + of the following API calls please double-check your + credentials. + Flavors and images ~~~~~~~~~~~~~~~~~~ @@ -378,6 +383,19 @@ To list the images that are available in your cloud, run some API calls: updated_at: '2014-10-15T22:42:52Z' visibility: public +.. only:: openstacksdk + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-2 + :end-before: step-3 + + You should see output something like this: + + .. code-block:: python + + 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) + ... + You can also get information about available flavors: @@ -480,6 +498,25 @@ You can also get information about available flavors: swap: '' vcpus: 1 +.. only:: openstacksdk + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-3 + :end-before: step-4 + + You should see output something like this: + + .. code-block:: python + + openstack.compute.v2.flavor.FlavorDetail(attrs={u'name': u'm1.tiny', u'links': [{u'href': u'http://controller:8774/v2/96ff6aa79e60423d9848b70d5475c415/flavors/1', u'rel': u'self'}, {u'href': u'http://controller:8774/96ff6aa79e60423d9848b70d5475c415/flavors/1', u'rel': u'bookmark'}], u'ram': 512, 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': 1, u'id': u'1'}, loaded=True) + + openstack.compute.v2.flavor.FlavorDetail(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, u'id': u'2'}, loaded=True) + + openstack.compute.v2.flavor.FlavorDetail(attrs={u'name': u'm1.medium', u'links': [{u'href': u'http://controller:8774/v2/96ff6aa79e60423d9848b70d5475c415/flavors/3', u'rel': u'self'}, {u'href': u'http://controller:8774/96ff6aa79e60423d9848b70d5475c415/flavors/3', u'rel': u'bookmark'}], u'ram': 4096, u'OS-FLV-DISABLED:disabled': False, u'vcpus': 2, u'swap': u'', u'os-flavor-access:is_public': True, u'rxtx_factor': 1.0, u'OS-FLV-EXT-DATA:ephemeral': 0, u'disk': 40, u'id': u'3'}, loaded=True) + + ... + + Your images and flavors will be different, of course. Choose an image and flavor for your instance. You need about 1GB RAM, 1 CPU, @@ -578,8 +615,19 @@ image that you picked in the previous section: updated_at: '2014-10-27T22:08:55Z' visibility: public +.. only:: openstacksdk -Next, choose which flavor you want to use: + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-4 + :end-before: step-5 + + You should see output something like this: + + .. code-block:: python + + 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) + +Next, tell the script which flavor you want to use: .. only:: fog @@ -666,6 +714,18 @@ Next, choose which flavor you want to use: swap: '' vcpus: 1 +.. only:: openstacksdk + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-5 + :end-before: step-6 + + You should see output something like this: + + .. code-block:: python + + 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) + Now, you're ready to launch the instance. Launch an instance @@ -705,14 +765,15 @@ Create the instance. .. only:: openstacksdk + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-6 + :end-before: step-7 + + You should see output something like: + .. code-block:: python - args = { - "name": "testing", - "flavorRef": flavor, - "imageRef": image, - } - instance = conn.compute.create_server(**args) + openstack.compute.v2.server.Server(attrs={'flavorRef': 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), 'name': 'testing', 'imageRef': openstack.image.v1.image.Image(attrs={u'name': u'ubuntu14.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), 'id': u'a1700b84-dc9a-434e-8f7a-40852e97781c'}, loaded=False) .. only:: pkgcloud @@ -782,6 +843,12 @@ If you list existing instances: :end-before: step-8 +.. only:: openstacksdk + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-7 + :end-before: step-8 + The new instance appears. .. only:: libcloud @@ -794,9 +861,7 @@ The new instance appears. .. code-block:: python - instances = conn.compute.list_servers() - for instance in instances: - print(instance) + openstack.compute.v2.server.ServerDetail(attrs={u'OS-EXT-STS:task_state': u'scheduling', u'addresses': {}, u'links': [{u'href': u'http://controller:8774/v2/96ff6aa79e60423d9848b70d5475c415/servers/a1700b84-dc9a-434e-8f7a-40852e97781c', u'rel': u'self'}, {u'href': u'http://controller:8774/96ff6aa79e60423d9848b70d5475c415/servers/a1700b84-dc9a-434e-8f7a-40852e97781c', u'rel': u'bookmark'}], u'image': {u'id': u'cb6b7936-d2c5-4901-8678-c88b3a6ed84c', u'links': [{u'href': u'http://controller:8774/96ff6aa79e60423d9848b70d5475c415/images/cb6b7936-d2c5-4901-8678-c88b3a6ed84c', u'rel': u'bookmark'}]}, u'OS-EXT-STS:vm_state': u'building', u'OS-SRV-USG:launched_at': None, u'flavor': {u'id': u'2', u'links': [{u'href': u'http://controller:8774/96ff6aa79e60423d9848b70d5475c415/flavors/2', u'rel': u'bookmark'}]}, u'id': u'a1700b84-dc9a-434e-8f7a-40852e97781c', u'user_id': u'59f76712914b44819cf311af43946079', 'imageRef': openstack.compute.v2.image.Image(attrs={u'id': u'cb6b7936-d2c5-4901-8678-c88b3a6ed84c', u'links': [{u'href': u'http://controller:8774/96ff6aa79e60423d9848b70d5475c415/images/cb6b7936-d2c5-4901-8678-c88b3a6ed84c', u'rel': u'bookmark'}]}, loaded=False), u'OS-DCF:diskConfig': u'MANUAL', u'accessIPv4': u'', u'accessIPv6': u'', u'progress': 0, u'OS-EXT-STS:power_state': 0, u'OS-EXT-AZ:availability_zone': u'nova', u'config_drive': u'', u'status': u'BUILD', u'updated': u'2015-10-12T13:45:37Z', u'hostId': u'', u'OS-SRV-USG:terminated_at': None, u'key_name': None, 'flavorRef': openstack.compute.v2.flavor.Flavor(attrs={u'id': u'2', u'links': [{u'href': u'http://controller:8774/96ff6aa79e60423d9848b70d5475c415/flavors/2', u'rel': u'bookmark'}]}, loaded=False), u'name': u'testing', u'created': u'2015-10-12T13:45:37Z', u'tenant_id': u'96ff6aa79e60423d9848b70d5475c415', u'os-extended-volumes:volumes_attached': [], u'metadata': {}}, loaded=True) .. only:: pkgcloud @@ -929,6 +994,12 @@ money. Destroy cloud resources to avoid unexpected expenses. :start-after: step-8 :end-before: step-9 +.. only:: openstacksdk + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :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 @@ -958,10 +1029,10 @@ instance: `these instructions `_ first. We'll cover these instructions in depth in :doc:`/introduction`. -.. only:: fog +In the following example, :code:`pub_key_file` should be set to the location +of your public SSH key file. - In the following example, :code:`pub_key_file` should be set to - the location of your public SSH key file. +.. only:: fog .. literalinclude:: ../samples/fog/getting_started.rb :language: ruby @@ -971,9 +1042,6 @@ instance: .. only:: libcloud - In the following example, :code:`pub_key_file` should be set to - the location of your public SSH key file. - .. literalinclude:: ../samples/libcloud/getting_started.py :start-after: step-9 :end-before: step-10 @@ -984,22 +1052,25 @@ instance: .. only:: pkgcloud - In the following example, :code:`pub_key_file` should be set to - the location of your public SSH key file. - .. literalinclude:: ../samples/pkgcloud/getting_started.js :start-after: step-9 :end-before: step-10 .. only:: shade - In the following example, :code:`pub_key_file` should be set to - the location of your public SSH key file. - .. literalinclude:: ../samples/shade/getting_started.py :start-after: step-9 :end-before: step-10 +.. only:: openstacksdk + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-9 + :end-before: step-10 + + .. code-block:: python + + openstack.compute.v2.keypair.Keypair(attrs={u'public_key': u'ssh-rsa ABAAABAQCyyzkyaPf.....', u'name': u'demokey', u'fingerprint': aa:bb:cc:... '}, loaded=True) * 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 @@ -1030,7 +1101,13 @@ instance: :start-after: step-10 :end-before: step-11 -* User data. During instance creation, you can provide user data to OpenStack to +.. only:: openstacksdk + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :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 chosen image. We'll go into more detail in :doc:`/introduction`. @@ -1060,6 +1137,14 @@ instance: :start-after: step-11 :end-before: step-12 +.. only:: openstacksdk + + .. note:: User data in openstacksdk must be encoded to base64 + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-11 + :end-before: step-12 + Now, you can boot and configure the instance. Boot and configure an instance @@ -1095,6 +1180,12 @@ request the instance, wait for it to build. :start-after: step-12 :end-before: step-13 +.. only:: openstacksdk + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :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. @@ -1148,7 +1239,6 @@ address to your instance. :start-after: step-14 :end-before: step-15 - .. only:: pkgcloud Use :code:`getFloatingIps` to check for unused addresses, selecting the @@ -1177,6 +1267,31 @@ address to your instance. :start-after: step-13 :end-before: step-14 +.. only:: openstacksdk + + .. note:: For this example we take Floating IP pool from network + which is called 'public'. This should be your external + network. + + List all available Floating IPs for this project and select the first free + one. Allocate new Floating IP if none is available. + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-13 + :end-before: step-14 + + This code returns the floating IP address: + + .. code-block:: python + + openstack.network.v2.floating_ip.FloatingIP(attrs={u'router_id': None, u'status': u'DOWN', u'tenant_id': u'96ff6aa79e60423d9848b70d5475c415', u'floating_network_id': u'0e43db46-8fd9-4ef1-8826-4cf9e809aede', u'fixed_ip_address': None, u'floating_ip_address': u'203.0.113.101', u'port_id': None, u'id': u'da890b1e-0afa-4724-9af6-0e5ab9cc33dd'}, loaded=True) + + You can then attach it to the instance: + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-14 + :end-before: step-15 + Run the script to start the deployment. @@ -1209,6 +1324,11 @@ by using your preferred browser. .. literalinclude:: ../samples/shade/getting_started.py :start-after: step-15 +.. only:: openstacksdk + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :start-after: step-15 + .. note:: If you do not use floating IPs, substitute another IP address as appropriate .. figure:: images/screenshot_webinterface.png @@ -1269,3 +1389,8 @@ information, the flavor ID, and image ID. .. literalinclude:: ../samples/libcloud/getting_started.py :language: python + +.. only:: openstacksdk + + .. literalinclude:: ../samples/openstacksdk/getting_started.py + :language: python diff --git a/tools/build-firstapp-rst.sh b/tools/build-firstapp-rst.sh index 88f8ed093..13acac5ed 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 pkgcloud shade; do +for tag in dotnet fog openstacksdk pkgcloud shade; do tools/build-rst.sh firstapp \ --tag ${tag} --target "draft/firstapp-${tag}" done diff --git a/tools/generatepot-rst.sh b/tools/generatepot-rst.sh index 23304447a..8276dda9d 100755 --- a/tools/generatepot-rst.sh +++ b/tools/generatepot-rst.sh @@ -51,7 +51,7 @@ if [ ${DOCNAME} = "install-guide" ] ; then TAG="-t obs -t rdo -t ubuntu -t debian" fi if [ ${DOCNAME} = "firstapp" ] ; then - TAG="-t libcloud -t dotnet -t fog -t pkgcloud -t shade" + TAG="-t libcloud -t dotnet -t fog -t openstacksdk -t pkgcloud -t shade" fi sphinx-build -b gettext $TAG ${DIRECTORY}/source/ \ ${DIRECTORY}/source/locale/