diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | block-storage.yaml | 6 | ||||
-rw-r--r-- | examples/scale1.yaml | 30 | ||||
-rw-r--r-- | examples/scale2.yaml | 66 | ||||
-rw-r--r-- | examples/scale_result.yaml | 190 | ||||
-rw-r--r-- | examples/source2_lib_result.yaml | 4 | ||||
-rw-r--r-- | examples/source_include_subkey_result.yaml | 4 | ||||
-rw-r--r-- | examples/source_lib_result.yaml | 4 | ||||
-rw-r--r-- | notcompute.yaml | 12 | ||||
-rw-r--r-- | nova-compute-instance.yaml | 20 | ||||
-rw-r--r-- | overcloud-source.yaml | 89 | ||||
-rw-r--r-- | ssl-source.yaml | 2 | ||||
-rw-r--r-- | swift-source.yaml | 4 | ||||
-rwxr-xr-x | test_merge.bash | 3 | ||||
-rw-r--r-- | tripleo_heat_merge/merge.py | 141 | ||||
-rw-r--r-- | undercloud-bm-source.yaml | 1 | ||||
-rw-r--r-- | undercloud-source.yaml | 22 | ||||
-rw-r--r-- | undercloud-vm-source.yaml | 1 |
18 files changed, 507 insertions, 96 deletions
@@ -10,8 +10,10 @@ overcloud_source_deps = nova-compute-instance.yaml all: $(generated_templates) +# Note that COMPUTESCALE is not a physical dep - 'make overcloud.yaml' won't do +# the right thing if you change COMPUTESCALE from one run to another. overcloud.yaml: overcloud-source.yaml swift-source.yaml ssl-source.yaml $(overcloud_source_deps) - python ./tripleo_heat_merge/merge.py overcloud-source.yaml swift-source.yaml ssl-source.yaml > $@.tmp + python ./tripleo_heat_merge/merge.py --scale NovaCompute=$${COMPUTESCALE:-'1'} overcloud-source.yaml swift-source.yaml ssl-source.yaml > $@.tmp mv $@.tmp $@ overcloud-with-block-storage.yaml: overcloud-source.yaml nova-compute-instance.yaml swift-source.yaml block-storage.yaml diff --git a/block-storage.yaml b/block-storage.yaml index 9959bdbc..48496356 100644 --- a/block-storage.yaml +++ b/block-storage.yaml @@ -6,13 +6,13 @@ Parameters: Default: overcloud-cinder-volume KeystoneHost: Type: String - Default: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ] } + Default: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ] } MySQLHost: Type: String - Default: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ] } + Default: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ] } RabbitHost: Type: String - Default: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ] } + Default: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ] } RabbitPassword: Type: String Default: "guest" diff --git a/examples/scale1.yaml b/examples/scale1.yaml new file mode 100644 index 00000000..c0a0763d --- /dev/null +++ b/examples/scale1.yaml @@ -0,0 +1,30 @@ +Resources: + ComputeUser: + Type: AWS::IAM::User + Properties: + Policies: [ { Ref: ComputeAccessPolicy } ] + GlobalAccessPolicy: + Type: OS::Heat::AccessPolicy + NovaCompute0Key: + Type: FileInclude + Path: examples/scale2.yaml + SubKey: Resources.NovaCompute0Key + NovaCompute0CompletionCondition: + Type: FileInclude + Path: examples/scale2.yaml + SubKey: Resources.NovaCompute0CompletionCondition + NovaCompute0CompletionHandle: + Type: FileInclude + Path: examples/scale2.yaml + SubKey: Resources.NovaCompute0CompletionHandle + NovaCompute0Config: + Type: FileInclude + Path: examples/scale2.yaml + SubKey: Resources.NovaCompute0Config + Parameters: + ComputeImage: "123" + RabbitPassword: "guest" + NovaCompute0: + Type: FileInclude + Path: examples/scale2.yaml + SubKey: Resources.NovaCompute0 diff --git a/examples/scale2.yaml b/examples/scale2.yaml new file mode 100644 index 00000000..d1a81fe5 --- /dev/null +++ b/examples/scale2.yaml @@ -0,0 +1,66 @@ +HeatTemplateFormatVersion: '2012-12-12' +Parameters: + ComputeImage: + Type: String + RabbitPassword: + Type: String + NoEcho: true +Resources: + ComputeAccessPolicy: + Type: OS::Heat::AccessPolicy + Properties: + AllowedResources: [ NovaCompute0 ] + NovaCompute0Key: + Type: AWS::IAM::AccessKey + Properties: + UserName: + Ref: ComputeUser + NovaCompute0CompletionCondition: + Type: AWS::CloudFormation::WaitCondition + DependsOn: notcompute + Properties: + Handle: {Ref: NovaCompute0CompletionHandle} + Count: '1' + Timeout: '1800' + NovaCompute0CompletionHandle: + Type: AWS::CloudFormation::WaitConditionHandle + NovaCompute0: + Type: OS::Nova::Server + Properties: + image: + Ref: ComputeImage + Metadata: + os-collect-config: + cfn: + access_key_id: + Ref: NovaCompute0Key + secret_access_key: + Fn::GetAtt: [ NovaCompute0Key, SecretAccessKey ] + stack_name: {Ref: 'AWS::StackName'} + path: NovaCompute0Config.Metadata + NovaCompute0Config: + Type: AWS::AutoScaling::LaunchConfiguration + Metadata: + completion-handle: + Ref: NovaCompute0CompletionHandle + os-collect-config: + cfn: + access_key_id: + Ref: NovaCompute0Key + secret_access_key: + Fn::GetAtt: [ NovaCompute0Key, SecretAccessKey ] + stack_name: {Ref: 'AWS::StackName'} + path: NovaCompute0Config.Metadata + neutron: + ovs: + local_ip: + Fn::Select: + - 0 + - Fn::Select: + - ctlplane + - Fn::GetAtt: + - NovaCompute0 + - networks + rabbit: + password: {Ref: RabbitPassword} + diff --git a/examples/scale_result.yaml b/examples/scale_result.yaml new file mode 100644 index 00000000..f22d864e --- /dev/null +++ b/examples/scale_result.yaml @@ -0,0 +1,190 @@ +Description: examples/scale1.yaml +HeatTemplateFormatVersion: '2012-12-12' +Resources: + ComputeUser: + Properties: + Policies: + - Ref: ComputeAccessPolicy + Type: AWS::IAM::User + GlobalAccessPolicy: + Type: OS::Heat::AccessPolicy + NovaCompute0: + Metadata: + os-collect-config: + cfn: + access_key_id: + Ref: NovaCompute0Key + path: NovaCompute0Config.Metadata + secret_access_key: + Fn::GetAtt: + - NovaCompute0Key + - SecretAccessKey + stack_name: + Ref: AWS::StackName + Properties: + image: + Ref: ComputeImage + Type: OS::Nova::Server + NovaCompute0CompletionCondition: + DependsOn: notcompute + Properties: + Count: '1' + Handle: + Ref: NovaCompute0CompletionHandle + Timeout: '1800' + Type: AWS::CloudFormation::WaitCondition + NovaCompute0CompletionHandle: + Type: AWS::CloudFormation::WaitConditionHandle + NovaCompute0Config: + Metadata: + completion-handle: + Ref: NovaCompute0CompletionHandle + neutron: + ovs: + local_ip: + Fn::Select: + - 0 + - Fn::Select: + - ctlplane + - Fn::GetAtt: + - NovaCompute0 + - networks + os-collect-config: + cfn: + access_key_id: + Ref: NovaCompute0Key + path: NovaCompute0Config.Metadata + secret_access_key: + Fn::GetAtt: + - NovaCompute0Key + - SecretAccessKey + stack_name: + Ref: AWS::StackName + rabbit: + password: guest + Type: AWS::AutoScaling::LaunchConfiguration + NovaCompute0Key: + Properties: + UserName: + Ref: ComputeUser + Type: AWS::IAM::AccessKey + NovaCompute1: + Metadata: + os-collect-config: + cfn: + access_key_id: + Ref: NovaCompute1Key + path: NovaCompute1Config.Metadata + secret_access_key: + Fn::GetAtt: + - NovaCompute1Key + - SecretAccessKey + stack_name: + Ref: AWS::StackName + Properties: + image: + Ref: ComputeImage + Type: OS::Nova::Server + NovaCompute1CompletionCondition: + DependsOn: notcompute + Properties: + Count: '1' + Handle: + Ref: NovaCompute1CompletionHandle + Timeout: '1800' + Type: AWS::CloudFormation::WaitCondition + NovaCompute1CompletionHandle: + Type: AWS::CloudFormation::WaitConditionHandle + NovaCompute1Config: + Metadata: + completion-handle: + Ref: NovaCompute1CompletionHandle + neutron: + ovs: + local_ip: + Fn::Select: + - 0 + - Fn::Select: + - ctlplane + - Fn::GetAtt: + - NovaCompute1 + - networks + os-collect-config: + cfn: + access_key_id: + Ref: NovaCompute1Key + path: NovaCompute1Config.Metadata + secret_access_key: + Fn::GetAtt: + - NovaCompute1Key + - SecretAccessKey + stack_name: + Ref: AWS::StackName + rabbit: + password: guest + Type: AWS::AutoScaling::LaunchConfiguration + NovaCompute1Key: + Properties: + UserName: + Ref: ComputeUser + Type: AWS::IAM::AccessKey + NovaCompute2: + Metadata: + os-collect-config: + cfn: + access_key_id: + Ref: NovaCompute2Key + path: NovaCompute2Config.Metadata + secret_access_key: + Fn::GetAtt: + - NovaCompute2Key + - SecretAccessKey + stack_name: + Ref: AWS::StackName + Properties: + image: + Ref: ComputeImage + Type: OS::Nova::Server + NovaCompute2CompletionCondition: + DependsOn: notcompute + Properties: + Count: '1' + Handle: + Ref: NovaCompute2CompletionHandle + Timeout: '1800' + Type: AWS::CloudFormation::WaitCondition + NovaCompute2CompletionHandle: + Type: AWS::CloudFormation::WaitConditionHandle + NovaCompute2Config: + Metadata: + completion-handle: + Ref: NovaCompute2CompletionHandle + neutron: + ovs: + local_ip: + Fn::Select: + - 0 + - Fn::Select: + - ctlplane + - Fn::GetAtt: + - NovaCompute2 + - networks + os-collect-config: + cfn: + access_key_id: + Ref: NovaCompute2Key + path: NovaCompute2Config.Metadata + secret_access_key: + Fn::GetAtt: + - NovaCompute2Key + - SecretAccessKey + stack_name: + Ref: AWS::StackName + rabbit: + password: guest + Type: AWS::AutoScaling::LaunchConfiguration + NovaCompute2Key: + Properties: + UserName: + Ref: ComputeUser + Type: AWS::IAM::AccessKey diff --git a/examples/source2_lib_result.yaml b/examples/source2_lib_result.yaml index d4b19768..172dce0f 100644 --- a/examples/source2_lib_result.yaml +++ b/examples/source2_lib_result.yaml @@ -1,7 +1,7 @@ Description: examples/source2.yaml HeatTemplateFormatVersion: '2012-12-12' Parameters: - GenericBImage: + BImage: Type: String ImportantValue: Default: a_default @@ -12,5 +12,5 @@ Resources: my_meta: Foo Properties: image: - Ref: GenericBImage + Ref: BImage Type: OS::Nova::Server diff --git a/examples/source_include_subkey_result.yaml b/examples/source_include_subkey_result.yaml index f5ff80ea..641e8148 100644 --- a/examples/source_include_subkey_result.yaml +++ b/examples/source_include_subkey_result.yaml @@ -1,7 +1,7 @@ Description: examples/source_include_subkey.yaml HeatTemplateFormatVersion: '2012-12-12' Parameters: - GenericBImage: + Foo: Type: String Resources: GenericB: @@ -10,5 +10,5 @@ Resources: Ref: ImportantValue Properties: image: - Ref: GenericBImage + Ref: Foo Type: OS::Nova::Server diff --git a/examples/source_lib_result.yaml b/examples/source_lib_result.yaml index a165cabf..ceb8a321 100644 --- a/examples/source_lib_result.yaml +++ b/examples/source_lib_result.yaml @@ -1,14 +1,14 @@ Description: examples/source.yaml HeatTemplateFormatVersion: '2012-12-12' Parameters: - AImage: null Default: my_image + SourceImage: null Type: String Resources: A: Properties: image: - Ref: AImage + Ref: SourceImage Type: OS::Nova::Server B: Metadata: diff --git a/notcompute.yaml b/notcompute.yaml index ef339361..fe66f3bb 100644 --- a/notcompute.yaml +++ b/notcompute.yaml @@ -78,9 +78,9 @@ Resources: AccessPolicy: Properties: AllowedResources: - - notcompute + - notCompute0 Type: OS::Heat::AccessPolicy - Key: + notCompute0Key: Properties: UserName: Ref: User @@ -90,7 +90,7 @@ Resources: Policies: - Ref: AccessPolicy Type: AWS::IAM::User - notcompute: + notCompute0: Metadata: OpenStack::ImageBuilder::Elements: - nova-api @@ -136,12 +136,12 @@ Resources: - {Ref: HeatDBPassword} - '@127.0.0.1/heat' access_key_id: - Ref: Key + Ref: notCompute0Key refresh: - - resource: notcompute + - resource: notCompute0 secret_key: Fn::GetAtt: - - Key + - notCompute0Key - SecretAccessKey stack: name: diff --git a/nova-compute-instance.yaml b/nova-compute-instance.yaml index 229615ad..4b6a8a05 100644 --- a/nova-compute-instance.yaml +++ b/nova-compute-instance.yaml @@ -83,19 +83,19 @@ Resources: Type: AWS::IAM::User Properties: Policies: [ { Ref: ComputeAccessPolicy } ] - ComputeKey: + NovaCompute0Key: Type: AWS::IAM::AccessKey Properties: UserName: Ref: ComputeUser - CompletionCondition: + NovaCompute0CompletionCondition: Type: AWS::CloudFormation::WaitCondition - DependsOn: notcompute + DependsOn: notCompute0 Properties: - Handle: {Ref: CompletionHandle} + Handle: {Ref: NovaCompute0CompletionHandle} Count: '1' Timeout: '1800' - CompletionHandle: + NovaCompute0CompletionHandle: Type: AWS::CloudFormation::WaitConditionHandle NovaCompute0: Type: OS::Nova::Server @@ -110,9 +110,9 @@ Resources: os-collect-config: cfn: access_key_id: - Ref: ComputeKey + Ref: NovaCompute0Key secret_access_key: - Fn::GetAtt: [ ComputeKey, SecretAccessKey ] + Fn::GetAtt: [ NovaCompute0Key, SecretAccessKey ] stack_name: {Ref: 'AWS::StackName'} path: NovaCompute0Config.Metadata OpenStack::ImageBuilder::Elements: [ nova-compute ] @@ -123,13 +123,13 @@ Resources: ImageId: '0' Metadata: completion-handle: - Ref: CompletionHandle + Ref: NovaCompute0CompletionHandle os-collect-config: cfn: access_key_id: - Ref: ComputeKey + Ref: NovaCompute0Key secret_access_key: - Fn::GetAtt: [ ComputeKey, SecretAccessKey ] + Fn::GetAtt: [ NovaCompute0Key, SecretAccessKey ] stack_name: {Ref: 'AWS::StackName'} path: NovaCompute0Config.Metadata nova: diff --git a/overcloud-source.yaml b/overcloud-source.yaml index 358de7d6..78ce4aa0 100644 --- a/overcloud-source.yaml +++ b/overcloud-source.yaml @@ -107,8 +107,8 @@ Resources: AccessPolicy: Properties: AllowedResources: - - notcompute - - notcomputeConfig + - notCompute0 + - notCompute0Config Type: OS::Heat::AccessPolicy ComputeAccessPolicy: Properties: @@ -116,25 +116,32 @@ Resources: - NovaCompute0 - NovaCompute0Config Type: OS::Heat::AccessPolicy - Key: + notCompute0Key: Properties: UserName: Ref: User Type: AWS::IAM::AccessKey - CompletionCondition: + notCompute0CompletionCondition: Type: AWS::CloudFormation::WaitCondition - DependsOn: notcompute + DependsOn: notCompute0 Properties: - Handle: {Ref: CompletionHandle} - Count: '2' + Handle: {Ref: notCompute0CompletionHandle} + Count: '1' Timeout: '1800' - CompletionHandle: + notCompute0CompletionHandle: Type: OS::Heat::UpdateWaitConditionHandle - ComputeKey: - Properties: - UserName: - Ref: ComputeUser - Type: AWS::IAM::AccessKey + NovaCompute0Key: + Type: FileInclude + Path: nova-compute-instance.yaml + SubKey: Resources.NovaCompute0Key + NovaCompute0CompletionCondition: + Type: FileInclude + Path: nova-compute-instance.yaml + SubKey: Resources.NovaCompute0CompletionCondition + NovaCompute0CompletionHandle: + Type: FileInclude + Path: nova-compute-instance.yaml + SubKey: Resources.NovaCompute0CompletionHandle ComputeUser: Properties: Policies: @@ -145,14 +152,14 @@ Resources: Path: nova-compute-instance.yaml SubKey: Resources.NovaCompute0Config Parameters: - NovaApiHost: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ] } - KeystoneHost: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ] } - RabbitHost: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ] } - NeutronHost: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ] } - GlanceHost: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ] } - NovaDSN: {"Fn::Join": ['', ['mysql://nova:unset@', {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ]}, '/nova']]} - CeilometerDSN: {"Fn::Join": ['', ['mysql://ceilometer:unset@', {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ]}, '/ceilometer']]} - NeutronDSN: {"Fn::Join": ['', ['mysql://neutron:unset@', {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notcompute, networks]} ]} ]}, '/neutron']]} + NovaApiHost: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ] } + KeystoneHost: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ] } + RabbitHost: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ] } + NeutronHost: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ] } + GlanceHost: {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ] } + NovaDSN: {"Fn::Join": ['', ['mysql://nova:unset@', {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ]}, '/nova']]} + CeilometerDSN: {"Fn::Join": ['', ['mysql://ceilometer:unset@', {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ]}, '/ceilometer']]} + NeutronDSN: {"Fn::Join": ['', ['mysql://neutron:unset@', {"Fn::Select": [ 0, {"Fn::Select": [ "ctlplane", {"Fn::GetAtt": [notCompute0, networks]} ]} ]}, '/neutron']]} NeutronNetworkType: "gre" NeutronEnableTunnelling: "True" NeutronNetworkVLANRanges: "" @@ -168,7 +175,7 @@ Resources: Policies: - Ref: AccessPolicy Type: AWS::IAM::User - notcomputeConfig: + notCompute0Config: Type: AWS::AutoScaling::LaunchConfiguration Properties: ImageId: '0' @@ -193,14 +200,14 @@ Resources: service-password: Ref: CinderPassword completion-handle: - Ref: CompletionHandle + Ref: notCompute0CompletionHandle controller-address: Fn::Select: - 0 - Fn::Select: - 'ctlplane' - Fn::GetAtt: - - notcompute + - notCompute0 - networks db-password: unset glance: @@ -212,7 +219,7 @@ Resources: - Fn::Select: - 'ctlplane' - Fn::GetAtt: - - notcompute + - notCompute0 - networks service-password: Ref: GlancePassword @@ -235,7 +242,7 @@ Resources: - Fn::Select: - 'ctlplane' - Fn::GetAtt: - - notcompute + - notCompute0 - networks - ':8003' metadata_server_url: @@ -247,7 +254,7 @@ Resources: - Fn::Select: - 'ctlplane' - Fn::GetAtt: - - notcompute + - notCompute0 - networks - ':8000' waitcondition_server_url: @@ -259,7 +266,7 @@ Resources: - Fn::Select: - 'ctlplane' - Fn::GetAtt: - - notcompute + - notCompute0 - networks - ':8000/v1/waitcondition' keystone: @@ -270,7 +277,7 @@ Resources: - Fn::Select: - 'ctlplane' - Fn::GetAtt: - - notcompute + - notCompute0 - networks neutron: host: @@ -279,7 +286,7 @@ Resources: - Fn::Select: - ctlplane - Fn::GetAtt: - - notcompute + - notCompute0 - networks metadata_proxy_shared_secret: unset ovs: @@ -290,7 +297,7 @@ Resources: - Fn::Select: - ctlplane - Fn::GetAtt: - - notcompute + - notCompute0 - networks bridge_mappings: {Ref: NeutronBridgeMappings} public_interface: @@ -320,7 +327,7 @@ Resources: - Fn::Select: - 'ctlplane' - Fn::GetAtt: - - notcompute + - notCompute0 - networks metadata-proxy: true service-password: @@ -328,11 +335,11 @@ Resources: os-collect-config: cfn: access_key_id: - Ref: Key - path: notcomputeConfig.Metadata + Ref: notCompute0Key + path: notCompute0Config.Metadata secret_access_key: Fn::GetAtt: - - Key + - notCompute0Key - SecretAccessKey stack_name: Ref: AWS::StackName @@ -343,13 +350,13 @@ Resources: - Fn::Select: - ctlplane - Fn::GetAtt: - - notcompute + - notCompute0 - networks password: guest ntp: servers: - {server: {Ref: NtpServer}, fudge: "stratum 0"} - notcompute: + notCompute0: Type: OS::Nova::Server Properties: image: @@ -364,11 +371,11 @@ Resources: os-collect-config: cfn: access_key_id: - Ref: Key - path: notcomputeConfig.Metadata + Ref: notCompute0Key + path: notCompute0Config.Metadata secret_access_key: Fn::GetAtt: - - Key + - notCompute0Key - SecretAccessKey stack_name: Ref: AWS::StackName @@ -384,6 +391,6 @@ Outputs: - Fn::Select: - ctlplane - Fn::GetAtt: - - notcompute + - notCompute0 - networks - :5000/v2.0/ diff --git a/ssl-source.yaml b/ssl-source.yaml index 9127555a..4fdd0f3b 100644 --- a/ssl-source.yaml +++ b/ssl-source.yaml @@ -11,7 +11,7 @@ Parameters: Type: String NoEcho: true Resources: - notcomputeConfig: + notCompute0Config: Type: AWS::AutoScaling::LaunchConfiguration Metadata: stunnel: diff --git a/swift-source.yaml b/swift-source.yaml index 14bbf2b9..0a6bf647 100644 --- a/swift-source.yaml +++ b/swift-source.yaml @@ -11,7 +11,7 @@ Parameters: Type: String NoEcho: true Resources: - notcomputeConfig: + notCompute0Config: Type: AWS::AutoScaling::LaunchConfiguration Metadata: swift: @@ -24,7 +24,7 @@ Resources: - Fn::Select: - 'ctlplane' - Fn::GetAtt: - - notcompute + - notCompute0 - networks - ':%PORT%/d1' hash: diff --git a/test_merge.bash b/test_merge.bash index 2f8a4cc9..f9bc7fb6 100755 --- a/test_merge.bash +++ b/test_merge.bash @@ -12,7 +12,7 @@ run_test() { local expected=$2 result=$(mktemp /tmp/test_merge.XXXXXX) fail=0 - $cmd > $result + $cmd --output $result if ! cmp $result $expected ; then diff -u $expected $result || : echo FAIL - $cmd result does not match expected @@ -28,6 +28,7 @@ run_test "python $merge_py examples/source.yaml" examples/source_lib_result.yaml run_test "python $merge_py examples/source2.yaml" examples/source2_lib_result.yaml run_test "python $merge_py examples/source_include_subkey.yaml" examples/source_include_subkey_result.yaml run_test "python $merge_py examples/launchconfig1.yaml examples/launchconfig2.yaml" examples/launchconfig_result.yaml +run_test "python $merge_py --scale NovaCompute=3 examples/scale1.yaml" examples/scale_result.yaml echo trap - EXIT exit $fail diff --git a/tripleo_heat_merge/merge.py b/tripleo_heat_merge/merge.py index 053a683a..dd254ab4 100644 --- a/tripleo_heat_merge/merge.py +++ b/tripleo_heat_merge/merge.py @@ -4,6 +4,97 @@ import yaml import argparse +def apply_scaling(template, scaling, in_copies=None): + """Apply a set of scaling operations to template. + + This is a single pass recursive function: for each call we process one + dict or list and recurse to handle children containers. + + Values are handled via scale_value. + + Keys in dicts are always copied per the scaling rule. + Values are either replaced or copied depending on whether the given + scaling rule is in in_copies. + """ + in_copies = dict(in_copies or {}) + # Shouldn't be needed but to avoid unexpected side effects/bugs we short + # circuit no-ops. + if not scaling: + return template + if isinstance(template, dict): + new_template = {} + for key, value in template.items(): + for prefix, copy_num, new_key in scale_value( + key, scaling, in_copies): + if prefix: + # e.g. Compute0, 1, Compute1Foo + in_copies[prefix] = prefix[:-1] + str(copy_num) + if isinstance(value, (dict, list)): + new_value = apply_scaling(value, scaling, in_copies) + new_template[new_key] = new_value + else: + new_values = list(scale_value(value, scaling, in_copies)) + # We have nowhere to multiply a non-container value of a + # dict, so it may be copied or unchanged but not scaled. + assert len(new_values) == 1 + new_template[new_key] = new_values[0][2] + if prefix: + del in_copies[prefix] + return new_template + elif isinstance(template, list): + new_template = [] + for value in template: + if isinstance(value, (dict, list)): + new_template.append(apply_scaling(value, scaling, in_copies)) + else: + for _, _, new_value in scale_value(value, scaling, in_copies): + new_template.append(new_value) + return new_template + else: + raise Exception("apply_scaling called with non-container %r" % template) + + +def scale_value(value, scaling, in_copies): + """Scale out a value. + + :param value: The value to scale (not a container). + :param scaling: The scaling map to use. + :param in_copies: What containers we're currently copying. + :return: An iterator of the new values for the value as tuples: + (prefix, copy_num, value). E.g. Compute0, 1, Compute1Foo + prefix and copy_num are only set when: + - a prefix in scaling matches value + - and that prefix is not in in_copies + """ + if isinstance(value, (str, unicode)): + for prefix, copies in scaling.items(): + if not value.startswith(prefix): + continue + suffix = value[len(prefix):] + if prefix in in_copies: + # Adjust to the copy number we're on + yield None, None, in_copies[prefix] + suffix + return + else: + for n in range(copies): + yield prefix, n, prefix[:-1] + str(n) + suffix + return + yield None, None, value + else: + yield None, None, value + + +def parse_scaling(scaling_args): + """Translate a list of scaling requests to a dict prefix:count.""" + scaling_args = scaling_args or [] + result = {} + for item in scaling_args: + key, value = item.split('=') + value = int(value) + result[key + '0'] = value + return result + + def _translate_role(role, master_role, slave_roles): if not master_role: return role @@ -89,14 +180,36 @@ def main(argv=None): parser.add_argument('--included-template-dir', nargs='?', default=INCLUDED_TEMPLATE_DIR, help='Path for resolving included templates') + parser.add_argument('--output', + help='File to write output to. - for stdout', + default='-') + parser.add_argument('--scale', action="append", + help="Names to scale out. Pass Prefix=1 to cause a key Prefix0Foo to " + "be copied to Prefix1Foo in the output, and value Prefix0Bar to be" + "renamed to Prefix1Bar inside that copy, or copied to Prefix1Bar " + "outside of any copy.") + parser.add_argument( + '--change-image-params', action='store_true', default=False, + help="Change parameters in templates to match resource names. This was " + " the default at one time but it causes issues when parameter " + " names need to remain stable.") args = parser.parse_args(argv) templates = args.templates + scaling = parse_scaling(args.scale) merged_template = merge(templates, args.master_role, args.slave_roles, - args.included_template_dir) - sys.stdout.write(merged_template) + args.included_template_dir, scaling=scaling, + change_image_params=args.change_image_params) + if args.output == '-': + out_file = sys.stdout + else: + out_file = file(args.output, 'wt') + out_file.write(merged_template) + def merge(templates, master_role=None, slave_roles=None, - included_template_dir=INCLUDED_TEMPLATE_DIR): + included_template_dir=INCLUDED_TEMPLATE_DIR, + scaling=None, change_image_params=None): + scaling = scaling or {} errors = [] end_template={'HeatTemplateFormatVersion': '2012-12-12', 'Description': []} @@ -132,11 +245,12 @@ def merge(templates, master_role=None, slave_roles=None, new_resources = template.get('Resources', {}) for r, rbody in sorted(new_resources.items()): if rbody['Type'] in MERGABLE_TYPES: - if 'image' in MERGABLE_TYPES[rbody['Type']]: - image_key = MERGABLE_TYPES[rbody['Type']]['image'] - # XXX Assuming ImageId is always a Ref - ikey_val = end_template['Parameters'][rbody['Properties'][image_key]['Ref']] - del end_template['Parameters'][rbody['Properties'][image_key]['Ref']] + if change_image_params: + if 'image' in MERGABLE_TYPES[rbody['Type']]: + image_key = MERGABLE_TYPES[rbody['Type']]['image'] + # XXX Assuming ImageId is always a Ref + ikey_val = end_template['Parameters'][rbody['Properties'][image_key]['Ref']] + del end_template['Parameters'][rbody['Properties'][image_key]['Ref']] role = rbody.get('Metadata', {}).get('OpenStack::Role', r) role = translate_role(role, master_role, slave_roles) if role != r: @@ -157,10 +271,11 @@ def merge(templates, master_role=None, slave_roles=None, if 'Resources' not in end_template: end_template['Resources'] = {} end_template['Resources'][role] = rbody - if 'image' in MERGABLE_TYPES[rbody['Type']]: - ikey = '%sImage' % (role) - end_template['Resources'][role]['Properties'][image_key] = {'Ref': ikey} - end_template['Parameters'][ikey] = ikey_val + if change_image_params: + if 'image' in MERGABLE_TYPES[rbody['Type']]: + ikey = '%sImage' % (role) + end_template['Resources'][role]['Properties'][image_key] = {'Ref': ikey} + end_template['Parameters'][ikey] = ikey_val elif rbody['Type'] == 'FileInclude': # we trust os.path.join to DTRT: if FileInclude path isn't # absolute, join to included_template_dir (./) @@ -185,6 +300,8 @@ def merge(templates, master_role=None, slave_roles=None, end_template['Resources'] = {} end_template['Resources'][r] = rbody + end_template = apply_scaling(end_template, scaling) + def fix_ref(item, old, new): if isinstance(item, dict): copy_item = dict(item) diff --git a/undercloud-bm-source.yaml b/undercloud-bm-source.yaml index 0f546c46..ff00d28c 100644 --- a/undercloud-bm-source.yaml +++ b/undercloud-bm-source.yaml @@ -22,7 +22,6 @@ Resources: ram_allocation_ratio: 1.0 reserved_host_memory_mb: 0 baremetal: - use_file_injection: "False" arch: Ref: BaremetalArch db: mysql://nova:unset@localhost/nova_bm diff --git a/undercloud-source.yaml b/undercloud-source.yaml index 6431dfd4..3ac148c9 100644 --- a/undercloud-source.yaml +++ b/undercloud-source.yaml @@ -33,7 +33,7 @@ Parameters: Description: The password for the Heat service account, used by the Heat services. Type: String NoEcho: true - Image: + undercloudImage: Default: undercloud Type: String NeutronPassword: @@ -52,19 +52,19 @@ Resources: AllowedResources: - undercloudConfig Type: OS::Heat::AccessPolicy - Key: + notCompute0Key: Properties: UserName: Ref: User Type: AWS::IAM::AccessKey - CompletionCondition: + notCompute0CompletionCondition: Type: AWS::CloudFormation::WaitCondition DependsOn: undercloud Properties: - Handle: {Ref: CompletionHandle} + Handle: {Ref: notCompute0CompletionHandle} Count: '1' Timeout: '1800' - CompletionHandle: + notCompute0CompletionHandle: Type: AWS::CloudFormation::WaitConditionHandle User: Properties: @@ -96,7 +96,7 @@ Resources: db: mysql://cinder:unset@localhost/cinder volume_size_mb: '5000' completion-handle: - Ref: CompletionHandle + Ref: notCompute0CompletionHandle db-password: unset glance: backend: file @@ -132,11 +132,11 @@ Resources: os-collect-config: cfn: access_key_id: - Ref: Key + Ref: notCompute0Key path: undercloudConfig.Metadata secret_access_key: Fn::GetAtt: - - Key + - notCompute0Key - SecretAccessKey stack_name: Ref: AWS::StackName @@ -165,7 +165,7 @@ Resources: Type: OS::Nova::Server Properties: image: - Ref: Image + Ref: undercloudImage flavor: Ref: Flavor key_name: @@ -174,11 +174,11 @@ Resources: os-collect-config: cfn: access_key_id: - Ref: Key + Ref: notCompute0Key path: undercloudConfig.Metadata secret_access_key: Fn::GetAtt: - - Key + - notCompute0Key - SecretAccessKey stack_name: Ref: AWS::StackName diff --git a/undercloud-vm-source.yaml b/undercloud-vm-source.yaml index a8987072..e70ef52d 100644 --- a/undercloud-vm-source.yaml +++ b/undercloud-vm-source.yaml @@ -26,7 +26,6 @@ Resources: ram_allocation_ratio: 1.0 reserved_host_memory_mb: 0 baremetal: - use_file_injection: "False" arch: Ref: BaremetalArch db: mysql://nova:unset@localhost/nova_bm |