Im dritten Teil zur Erstellung des Skripts werden noch die Server angelegt, ein Server wird auf eine öffentliche IP per NAT gelegt, einige Parameter abgefragt und die wichtigsten Daten mit ausgegeben.
Die Vorlage für Server ist umfangreicher:
type: OS::Nova::Server properties: admin_pass: String availability_zone: String block_device_mapping: [{"volume_size": Integer, "volume_id": String, "snapshot_id": String, "delete_on_termination": Boolean, "device_name": String}, {"volume_size": Integer, "volume_id": String, "snapshot_id": String, "delete_on_termination": Boolean, "device_name": String}, ...] config_drive: Boolean diskConfig: String flavor: String flavor_update_policy: String image: String image_update_policy: String key_name: String metadata: {...} name: String networks: [{"port": String, "fixed_ip": String, "uuid": String, "network": String}, {"port": String, "fixed_ip": String, "uuid": String, "network": String}, ...] personality: {...} reservation_id: String scheduler_hints: {...} security_groups: [Value, Value, ...] software_config_transport: String user_data: String user_data_format: String
Eine Kurzbeschreibung der Parameter findet sich in der OpenStack-Dokumentation.
Für dieses Beispiel sind nur einige wenige Angaben notwendig. Über „key_name“ wird der Name des Schlüsselpaares aus den Eingangs-Parametern abgefragt. Die Art (Größe) des Servers, wird über den Parameter „flavor“ ausgewählt. „image“ ist hier über eine ID zu einem Ubuntu14-Image selektiert. Die „networks“ werden an „port“ und „network“ gebunden, die im zweiten Teil des Skripts definiert wurden: Server1 an Port1, Server2 an Port2, Server3 an Port3.
Um nach dem Booten des Servers noch Konfigurationen durchzuführen, wird bei Server1 über „user_data“ ein Skript ausgeführt.
So ergeben sich die drei Skripte für die Server zu:
my_server1: type: OS::Nova::Server properties: name: WebServer1 key_name: { get_param: keypair_name } flavor: { get_param: machine_flavor } image: { get_param: image_id } networks: [{ "port": { get_resource: my_port1 }, "network": { get_resource: my_first_network }}] user_data: | #!/bin/bash /bin/touch /tmp/nova-was-here my_server2: type: OS::Nova::Server properties: name: DBServer1 key_name: { get_param: keypair_name } flavor: { get_param: machine_flavor } image: { get_param: image_id } networks: [{ "port": { get_resource: my_port2 }, "network": { get_resource: my_first_network }}] my_server3: type: OS::Nova::Server properties: name: DBServer2 key_name: { get_param: keypair_name } flavor: { get_param: machine_flavor } image: { get_param: image_id } networks: [{ "port": { get_resource: my_port3 }, "network": { get_resource: my_first_network }}]
Für den Zugriff auf den Server1 von außen ist noch die Vergabe einer echten IP notwendig. Dies geschieht über
my_floatingip: type: OS::Neutron::FloatingIP properties: floating_network_id: { get_param: external_net_id } port_id: { get_resource: my_port1 }
Wobei die ID für das externe Netzwerk in jeder Installation verschieden ist und von Nicht-Admins nicht dynamisch abgefragt werden kann.
Nützlich ist noch die Ausgabe der Attribute für die nun erzeugten Ressourcen. Hierfür kann die „outputs“-Sektion des Skripts benutzt werden:
outputs: ServerIP: value: str_replace: template: Öffentliche IP ist hostip params: hostip: { get_attr: [my_floatingip, floating_ip_address] } FloatingIP: value: { get_attr: [my_floatingip, show ] } Network: value: { get_attr: [my_first_network, show] } Subnet: value: { get_attr: [my_first_subnet, show] } Server1: value: { get_attr: [ my_server1, show ] } Server2: value: { get_attr: [ my_server2, show ] } Server3: value: { get_attr: [ my_server3, show ] } Port1: value: { get_attr: [ my_port1, show ] } Port4: value: { get_attr: [ my_port4, show ] }
ServerIP fragt exemplarisch die vergebene öffentliche IP ab und gibt diese aus. „hostip“ ist hier eine Variable. Mittels der Funktion „str_replace“ wird der Parameter „hostip“ im Template dynamisch durch die vergebene IP-Adresse ersetzt.
Über „get_attr“ lassen sich für die erzeugten Ressourcen Attribute abfragen. Über das Attribut „show“ werden alle Attribute ausgegeben, es lassen sich jedoch auch einzelne Angaben gezielt auswählen. Eine Liste der möglichen Attribute findet sich bei jedem Objekt in der Doku jeweils unter dem Bereich „Attributes“.
Der vollständige Stack sieht dann in Horizon schon komplexer aus:
Das vollständige Skript ist nun schon recht lang geworden:
heat_template_version: 2013-05-23 description: My First Heat Template parameter_groups: - label: IP Settings description: Groups all the IPv4 settings parameters: - cidr_net - ip_start - ip_end - gateway_address - dns_server1 - dns_server2 - label: Open Stack Settings description: All settings about OpenStack parameters: - keypair_name - image_id - external_net_id - machine_flavor parameters: cidr_net: type: string default: 192.168.0.0/24 description: Size of the ip-net dns_server1: type: string default: 141.20.1.3 description: DNS 1 for this stack dns_server2: type: string default: 141.20.1.31 description: DNS 2 for this stack ip_start: type: string default: 192.168.0.100 description: DHCP start address for this stack ip_end: type: string default: 192.168.0.200 description: DHCP end address for this stack gateway_address: type: string default: 192.168.0.1 description: IP address of the gateway keypair_name: type: string default: MyNewKeys description: Which keypair should be used image_id: type: string default: YOUR_IMAGE_ID_HERE description: Put your favorite image ID here external_net_id: type: string default: YOUR_EXT_NET_ID_HERE description: Ask your administrator for the external_network_id machine_flavor: type: string default: m1.small description: Your machine flavor to be used for this server. constraints: - allowed_values: [m1.nano, m1.tiny, m1.small, m1.large] description: Value must be one of 'm1.tiny', 'm1.small' or 'm1.large' resources: my_first_network: type: OS::Neutron::Net properties: name: My1Net my_first_subnet: type: OS::Neutron::Subnet properties: allocation_pools: [ { start: { get_param: ip_start }, end: { get_param: ip_end } } ] cidr: { get_param: cidr_net } dns_nameservers: [ { get_param: dns_server1 }, { get_param: dns_server2 } ] enable_dhcp: true gateway_ip: { get_param: gateway_address } network_id: { get_resource: my_first_network } name: My1Subnet my_basic_security_group: type: OS::Neutron::SecurityGroup properties: description: Allow ICMP, SSH name: BasicRules rules: [ {remote_ip_prefix: 0.0.0.0/0, direction: ingress, protocol: tcp, port_range_min: 22, port_range_max: 22}, {remote_ip_prefix: 0.0.0.0/0, direction: ingress, protocol: icmp}, {remote_ip_prefix: 0.0.0.0/0, direction: egress, protocol: icmp}] my_http_security_group: type: OS::Neutron::SecurityGroup properties: description: Allow HTTP name: HTTPRules rules: [ {remote_ip_prefix: 0.0.0.0/0, direction: ingress, protocol: tcp, port_range_min: 80, port_range_max: 80}] my_router_security_group: type: OS::Neutron::SecurityGroup properties: description: Allow All name: RouterRules rules: [ {remote_ip_prefix: 0.0.0.0/0, direction: ingress}, {remote_ip_prefix: 0.0.0.0/0, direction: egress}] my_port1: type: OS::Neutron::Port properties: admin_state_up: true name: Port01 network_id: { get_resource: my_first_network } security_groups: [ {get_resource: my_basic_security_group }, { get_resource: my_http_security_group } ] my_port2: type: OS::Neutron::Port properties: admin_state_up: true name: Port02 network_id: { get_resource: my_first_network } security_groups: [ { get_resource: my_basic_security_group } ] my_port3: type: OS::Neutron::Port properties: admin_state_up: true name: Port03 network_id: { get_resource: my_first_network } security_groups: [ { get_resource: my_basic_security_group } ] my_port4: type: OS::Neutron::Port properties: admin_state_up: true name: Port04 network_id: { get_resource: my_first_network } fixed_ips: [{ "subnet_id": { get_resource: my_first_subnet }, "ip_address": { get_param: gateway_address } }] security_groups: [ { get_resource: my_router_security_group } ] my_router: type: OS::Neutron::Router properties: admin_state_up: true name: MyRouter my_router_gateway: type: OS::Neutron::RouterGateway properties: network_id: { get_param: external_net_id } router_id: { get_resource: my_router } my_router_int: type: OS::Neutron::RouterInterface properties: router_id: { get_resource: my_router } port_id: { get_resource: my_port4 } my_server1: type: OS::Nova::Server properties: name: WebServer1 key_name: { get_param: keypair_name } flavor: { get_param: machine_flavor } image: { get_param: image_id } networks: [{ "port": { get_resource: my_port1 }, "network": { get_resource: my_first_network }}] user_data: | #!/bin/bash /bin/touch /tmp/nova-was-here my_server2: type: OS::Nova::Server properties: name: DBServer1 key_name: { get_param: keypair_name } flavor: { get_param: machine_flavor } image: { get_param: image_id } networks: [{ "port": { get_resource: my_port2 }, "network": { get_resource: my_first_network }}] my_server3: type: OS::Nova::Server properties: name: DBServer2 key_name: { get_param: keypair_name } flavor: { get_param: machine_flavor } image: { get_param: image_id } networks: [{ "port": { get_resource: my_port3 }, "network": { get_resource: my_first_network }}] my_floatingip: type: OS::Neutron::FloatingIP properties: floating_network_id: { get_param: external_net_id } port_id: { get_resource: my_port1 } outputs: ServerIP: value: str_replace: template: Öffentliche IP ist hostip params: hostip: { get_attr: [my_floatingip, floating_ip_address] } FloatingIP: value: { get_attr: [my_floatingip, show ] } Network: value: { get_attr: [my_first_network, show] } Subnet: value: { get_attr: [my_first_subnet, show] } Server1: value: { get_attr: [ my_server1, show ] } Server2: value: { get_attr: [ my_server2, show ] } Server3: value: { get_attr: [ my_server3, show ] } Port1: value: { get_attr: [ my_port1, show ] } Port4: value: { get_attr: [ my_port4, show ] }