From ac6a0dedecf0b8afc332cf114211a82db88eef17 Mon Sep 17 00:00:00 2001 From: Stan Lagun Date: Tue, 8 Apr 2014 14:08:56 +0400 Subject: [PATCH] Added 'destroy' method that is called on deleted instances Added ability to modify/remove data from structures (like Heat templates) via jsonpatch and thus added ability to clean up Heat resources that was obtained by deleted instances Closes bug: #1296624 Change-Id: I4db226a5ab00ff363f8b5d44a5d690df942622e8 --- contrib/devstack/lib/murano | 4 +- .../Classes/ActiveDirectory.yaml} | 11 ++- .../Classes/Controller.yaml} | 4 +- .../Classes/DomainHost.yaml | 20 +++++ .../Classes/Host.yaml | 49 +++++++++++ .../Classes/PrimaryController.yaml} | 4 +- .../Classes/SecondaryController.yaml} | 3 +- .../Resources/AskDnsIp.template | 0 .../Resources/CreatePrimaryDC.template | 0 .../Resources/CreateSecondaryDC.template | 0 .../Resources/JoinDomain.template | 25 ++++++ .../Resources/SetPassword.template | 17 ++++ .../scripts/Get-DnsListeningIpAddress.ps1 | 0 .../Resources/scripts/ImportCoreFunctions.ps1 | 0 .../Install-RolePrimaryDomainController.ps1 | 0 .../Install-RoleSecondaryDomainController.ps1 | 0 .../Resources/scripts/Join-Domain.ps1 | 67 +++++++++++++++ .../scripts/Set-LocalUserPassword.ps1 | 37 ++++++++ .../UI/ui.yaml | 0 .../logo2.png | Bin .../manifest.yaml | 27 ++++++ .../Classes/Application.yaml} | 0 .../Classes/Environment.yaml} | 0 .../Classes/Object.yaml} | 0 .../io.murano/Classes/resources/Instance.yaml | 81 ++++++++++++++++++ meta/io.murano/Resources/Agent-v1.template | 36 ++++++++ meta/io.murano/Resources/Agent-v2.template | 35 ++++++++ meta/io.murano/Resources/linux-init.sh | 11 +++ meta/io.murano/Resources/windows-init.ps1 | 68 +++++++++++++++ meta/io.murano/manifest.yaml | 21 +++++ meta/packages/activedirectory/manifest.yaml | 16 ---- meta/packages/core/manifest.yaml | 12 --- muranoapi/cmd/manage.py | 4 + muranoapi/common/engine.py | 3 +- muranoapi/db/services/environments.py | 12 +-- muranoapi/dsl/executor.py | 49 ++++++++++- muranoapi/dsl/object_store.py | 5 ++ muranoapi/dsl/results_serializer.py | 14 ++- muranoapi/engine/system/heat_stack.py | 32 ++++--- muranoapi/engine/system/yaql_functions.py | 15 ++++ muranoapi/openstack/common/exception.py | 2 +- requirements.txt | 1 + 42 files changed, 618 insertions(+), 67 deletions(-) rename meta/{packages/activedirectory/Classes/ad.yaml => io.murano.windows.ActiveDirectory/Classes/ActiveDirectory.yaml} (72%) rename meta/{packages/activedirectory/Classes/controller.yaml => io.murano.windows.ActiveDirectory/Classes/Controller.yaml} (75%) create mode 100644 meta/io.murano.windows.ActiveDirectory/Classes/DomainHost.yaml create mode 100644 meta/io.murano.windows.ActiveDirectory/Classes/Host.yaml rename meta/{packages/activedirectory/Classes/primary_controller.yaml => io.murano.windows.ActiveDirectory/Classes/PrimaryController.yaml} (80%) rename meta/{packages/activedirectory/Classes/secondary_controller.yaml => io.murano.windows.ActiveDirectory/Classes/SecondaryController.yaml} (82%) rename meta/{packages/activedirectory => io.murano.windows.ActiveDirectory}/Resources/AskDnsIp.template (100%) rename meta/{packages/activedirectory => io.murano.windows.ActiveDirectory}/Resources/CreatePrimaryDC.template (100%) rename meta/{packages/activedirectory => io.murano.windows.ActiveDirectory}/Resources/CreateSecondaryDC.template (100%) create mode 100644 meta/io.murano.windows.ActiveDirectory/Resources/JoinDomain.template create mode 100644 meta/io.murano.windows.ActiveDirectory/Resources/SetPassword.template rename meta/{packages/activedirectory => io.murano.windows.ActiveDirectory}/Resources/scripts/Get-DnsListeningIpAddress.ps1 (100%) rename meta/{packages/activedirectory => io.murano.windows.ActiveDirectory}/Resources/scripts/ImportCoreFunctions.ps1 (100%) rename meta/{packages/activedirectory => io.murano.windows.ActiveDirectory}/Resources/scripts/Install-RolePrimaryDomainController.ps1 (100%) rename meta/{packages/activedirectory => io.murano.windows.ActiveDirectory}/Resources/scripts/Install-RoleSecondaryDomainController.ps1 (100%) create mode 100644 meta/io.murano.windows.ActiveDirectory/Resources/scripts/Join-Domain.ps1 create mode 100644 meta/io.murano.windows.ActiveDirectory/Resources/scripts/Set-LocalUserPassword.ps1 rename meta/{packages/activedirectory => io.murano.windows.ActiveDirectory}/UI/ui.yaml (100%) rename meta/{packages/activedirectory => io.murano.windows.ActiveDirectory}/logo2.png (100%) create mode 100644 meta/io.murano.windows.ActiveDirectory/manifest.yaml rename meta/{packages/core/Classes/application.yaml => io.murano/Classes/Application.yaml} (100%) rename meta/{packages/core/Classes/environment.yaml => io.murano/Classes/Environment.yaml} (100%) rename meta/{packages/core/Classes/object.yaml => io.murano/Classes/Object.yaml} (100%) create mode 100644 meta/io.murano/Classes/resources/Instance.yaml create mode 100644 meta/io.murano/Resources/Agent-v1.template create mode 100644 meta/io.murano/Resources/Agent-v2.template create mode 100644 meta/io.murano/Resources/linux-init.sh create mode 100644 meta/io.murano/Resources/windows-init.ps1 create mode 100644 meta/io.murano/manifest.yaml delete mode 100644 meta/packages/activedirectory/manifest.yaml delete mode 100644 meta/packages/core/manifest.yaml diff --git a/contrib/devstack/lib/murano b/contrib/devstack/lib/murano index 0e6380bad..9a48abf2d 100644 --- a/contrib/devstack/lib/murano +++ b/contrib/devstack/lib/murano @@ -134,8 +134,8 @@ function init_murano() { recreate_database murano utf8 $MURANO_BIN_DIR/murano-manage --config-file $MURANO_CONF_FILE db-sync - $MURANO_BIN_DIR/murano-manage --config-file $MURANO_CONF_FILE import-package $MURANO_DIR/meta/packages/core - $MURANO_BIN_DIR/murano-manage --config-file $MURANO_CONF_FILE import-package $MURANO_DIR/meta/packages/activedirectory + $MURANO_BIN_DIR/murano-manage --config-file $MURANO_CONF_FILE import-package $MURANO_DIR/meta/io.murano + $MURANO_BIN_DIR/murano-manage --config-file $MURANO_CONF_FILE import-package $MURANO_DIR/meta/io.murano.windows.ActiveDirectory } diff --git a/meta/packages/activedirectory/Classes/ad.yaml b/meta/io.murano.windows.ActiveDirectory/Classes/ActiveDirectory.yaml similarity index 72% rename from meta/packages/activedirectory/Classes/ad.yaml rename to meta/io.murano.windows.ActiveDirectory/Classes/ActiveDirectory.yaml index 05125276f..7f037ef71 100644 --- a/meta/packages/activedirectory/Classes/ad.yaml +++ b/meta/io.murano.windows.ActiveDirectory/Classes/ActiveDirectory.yaml @@ -1,8 +1,7 @@ Namespaces: - =: io.murano.services.windows.activeDirectory + =: io.murano.windows.activeDirectory std: io.murano sys: io.murano.system - win: io.murano.services.windows Name: ActiveDirectory @@ -29,5 +28,9 @@ Properties: Workflow: deploy: Body: - - $.primaryController.deploy() - - $.secondaryControllers.pselect($.deploy()) + - $.primaryController.deploy() + - $.secondaryControllers.pselect($.deploy()) + - $.reportDeployed() + + destroy: + - $.reportDestroyed() diff --git a/meta/packages/activedirectory/Classes/controller.yaml b/meta/io.murano.windows.ActiveDirectory/Classes/Controller.yaml similarity index 75% rename from meta/packages/activedirectory/Classes/controller.yaml rename to meta/io.murano.windows.ActiveDirectory/Classes/Controller.yaml index 0e5deb6bf..76fdb4656 100644 --- a/meta/packages/activedirectory/Classes/controller.yaml +++ b/meta/io.murano.windows.ActiveDirectory/Classes/Controller.yaml @@ -1,8 +1,8 @@ Namespaces: - =: io.murano.services.windows.activeDirectory + =: io.murano.windows.activeDirectory std: io.murano sys: io.murano.system - win: io.murano.services.windows + win: io.murano.windows Name: Controller diff --git a/meta/io.murano.windows.ActiveDirectory/Classes/DomainHost.yaml b/meta/io.murano.windows.ActiveDirectory/Classes/DomainHost.yaml new file mode 100644 index 000000000..9633f9bbc --- /dev/null +++ b/meta/io.murano.windows.ActiveDirectory/Classes/DomainHost.yaml @@ -0,0 +1,20 @@ +Namespaces: + =: io.murano.windows + ad: io.murano.windows.activeDirectory + +Name: DomainHost + +Extends: Host + +Properties: + domain: + Contract: $.class(ad:ActiveDirectory).notNull() + +Workflow: + deploy: + Arguments: + Body: + - $.super($.deploy()) + #- $.joinDomain($.domain) + # Workaround against broken ResourceManager: + - $.super($.joinDomain($this.domain)) diff --git a/meta/io.murano.windows.ActiveDirectory/Classes/Host.yaml b/meta/io.murano.windows.ActiveDirectory/Classes/Host.yaml new file mode 100644 index 000000000..9294e79dd --- /dev/null +++ b/meta/io.murano.windows.ActiveDirectory/Classes/Host.yaml @@ -0,0 +1,49 @@ +Namespaces: + =: io.murano.windows + ad: io.murano.windows.activeDirectory + res: io.murano.resources + sys: io.murano.system + +Name: Host + +Extends: res:Instance + +Properties: + adminAccountName: + Contract: $.string().notNull() + Default: Administrator + + adminPassword: + Contract: $.string().notNull() + +Workflow: + initialize: + Body: + - $.super($.initialize()) + + deploy: + Body: + - $.super($.deploy()) + + - $resources: new(sys:Resources) + - $template: $resources.json('SetPassword.template').bind(dict( + adminPassword => $.adminPassword + )) + - $.agent.send($template, $resources) + + joinDomain: + Arguments: + - domain: + Contract: $.class(ad:ActiveDirectory).notNull() + Body: + + - $resources: new(sys:Resources) + - $template: $resources.json('JoinDomain.template').bind(dict( + domain => $domain.name, + domainUser => $domain.adminAccountName, + domainPassword => $domain.adminPassword, + ouPath => '', + dnsIp => $domain.primaryController.dnsIp + )) + - $.agent.call($template, $resources) + diff --git a/meta/packages/activedirectory/Classes/primary_controller.yaml b/meta/io.murano.windows.ActiveDirectory/Classes/PrimaryController.yaml similarity index 80% rename from meta/packages/activedirectory/Classes/primary_controller.yaml rename to meta/io.murano.windows.ActiveDirectory/Classes/PrimaryController.yaml index 008c657ca..30883f6b3 100644 --- a/meta/packages/activedirectory/Classes/primary_controller.yaml +++ b/meta/io.murano.windows.ActiveDirectory/Classes/PrimaryController.yaml @@ -1,8 +1,7 @@ Namespaces: - =: io.murano.services.windows.activeDirectory + =: io.murano.windows.activeDirectory std: io.murano sys: io.murano.system - win: io.murano.services.windows Name: PrimaryController @@ -22,7 +21,6 @@ Workflow: deploy: Arguments: Body: - - $.debugPrint('Deploying Primary Controller for domain {0}'.format($this.domain.name)) - $.super($.deploy()) - $resources: new(io.murano.system.Resources) - $template: $resources.json('CreatePrimaryDC.template').bind(dict( diff --git a/meta/packages/activedirectory/Classes/secondary_controller.yaml b/meta/io.murano.windows.ActiveDirectory/Classes/SecondaryController.yaml similarity index 82% rename from meta/packages/activedirectory/Classes/secondary_controller.yaml rename to meta/io.murano.windows.ActiveDirectory/Classes/SecondaryController.yaml index b57d8c2a6..ebd346645 100644 --- a/meta/packages/activedirectory/Classes/secondary_controller.yaml +++ b/meta/io.murano.windows.ActiveDirectory/Classes/SecondaryController.yaml @@ -1,5 +1,5 @@ Namespaces: - =: io.murano.services.windows.activeDirectory + =: io.murano.windows.activeDirectory std: io.murano sys: io.murano.system @@ -15,7 +15,6 @@ Workflow: deploy: Body: - - $.debugPrint('Deploying Secondary Controller for domain {0}'.format($this.domain.name)) - $.super($.deploy()) - $.host.joinDomain($.domain) - $resources: new(sys:Resources) diff --git a/meta/packages/activedirectory/Resources/AskDnsIp.template b/meta/io.murano.windows.ActiveDirectory/Resources/AskDnsIp.template similarity index 100% rename from meta/packages/activedirectory/Resources/AskDnsIp.template rename to meta/io.murano.windows.ActiveDirectory/Resources/AskDnsIp.template diff --git a/meta/packages/activedirectory/Resources/CreatePrimaryDC.template b/meta/io.murano.windows.ActiveDirectory/Resources/CreatePrimaryDC.template similarity index 100% rename from meta/packages/activedirectory/Resources/CreatePrimaryDC.template rename to meta/io.murano.windows.ActiveDirectory/Resources/CreatePrimaryDC.template diff --git a/meta/packages/activedirectory/Resources/CreateSecondaryDC.template b/meta/io.murano.windows.ActiveDirectory/Resources/CreateSecondaryDC.template similarity index 100% rename from meta/packages/activedirectory/Resources/CreateSecondaryDC.template rename to meta/io.murano.windows.ActiveDirectory/Resources/CreateSecondaryDC.template diff --git a/meta/io.murano.windows.ActiveDirectory/Resources/JoinDomain.template b/meta/io.murano.windows.ActiveDirectory/Resources/JoinDomain.template new file mode 100644 index 000000000..3d8cbeff8 --- /dev/null +++ b/meta/io.murano.windows.ActiveDirectory/Resources/JoinDomain.template @@ -0,0 +1,25 @@ +{ + "Scripts": [ + "ImportCoreFunctions.ps1", + "Join-Domain.ps1" + ], + "Commands": [ + { + "Name": "Set-NetworkAdapterConfiguration", + "Arguments": { + "FirstAvailable": true, + "DNSServer": "$dnsIp" + } + }, + { + "Name": "Join-Domain", + "Arguments": { + "Username": "$domainUser", + "Password": "$domainPassword", + "DomainName": "$domain", + "OUPath": "$ouPath" + } + } + ], + "RebootOnCompletion": 1 +} \ No newline at end of file diff --git a/meta/io.murano.windows.ActiveDirectory/Resources/SetPassword.template b/meta/io.murano.windows.ActiveDirectory/Resources/SetPassword.template new file mode 100644 index 000000000..101db0ead --- /dev/null +++ b/meta/io.murano.windows.ActiveDirectory/Resources/SetPassword.template @@ -0,0 +1,17 @@ +{ + "Scripts": [ + "ImportCoreFunctions.ps1", + "Set-LocalUserPassword.ps1" + ], + "Commands": [ + { + "Name": "Set-LocalUserPassword", + "Arguments": { + "UserName": "Administrator", + "Password": "$adminPassword", + "Force": true + } + } + ], + "RebootOnCompletion": 0 +} \ No newline at end of file diff --git a/meta/packages/activedirectory/Resources/scripts/Get-DnsListeningIpAddress.ps1 b/meta/io.murano.windows.ActiveDirectory/Resources/scripts/Get-DnsListeningIpAddress.ps1 similarity index 100% rename from meta/packages/activedirectory/Resources/scripts/Get-DnsListeningIpAddress.ps1 rename to meta/io.murano.windows.ActiveDirectory/Resources/scripts/Get-DnsListeningIpAddress.ps1 diff --git a/meta/packages/activedirectory/Resources/scripts/ImportCoreFunctions.ps1 b/meta/io.murano.windows.ActiveDirectory/Resources/scripts/ImportCoreFunctions.ps1 similarity index 100% rename from meta/packages/activedirectory/Resources/scripts/ImportCoreFunctions.ps1 rename to meta/io.murano.windows.ActiveDirectory/Resources/scripts/ImportCoreFunctions.ps1 diff --git a/meta/packages/activedirectory/Resources/scripts/Install-RolePrimaryDomainController.ps1 b/meta/io.murano.windows.ActiveDirectory/Resources/scripts/Install-RolePrimaryDomainController.ps1 similarity index 100% rename from meta/packages/activedirectory/Resources/scripts/Install-RolePrimaryDomainController.ps1 rename to meta/io.murano.windows.ActiveDirectory/Resources/scripts/Install-RolePrimaryDomainController.ps1 diff --git a/meta/packages/activedirectory/Resources/scripts/Install-RoleSecondaryDomainController.ps1 b/meta/io.murano.windows.ActiveDirectory/Resources/scripts/Install-RoleSecondaryDomainController.ps1 similarity index 100% rename from meta/packages/activedirectory/Resources/scripts/Install-RoleSecondaryDomainController.ps1 rename to meta/io.murano.windows.ActiveDirectory/Resources/scripts/Install-RoleSecondaryDomainController.ps1 diff --git a/meta/io.murano.windows.ActiveDirectory/Resources/scripts/Join-Domain.ps1 b/meta/io.murano.windows.ActiveDirectory/Resources/scripts/Join-Domain.ps1 new file mode 100644 index 000000000..403ef798d --- /dev/null +++ b/meta/io.murano.windows.ActiveDirectory/Resources/scripts/Join-Domain.ps1 @@ -0,0 +1,67 @@ + +trap { + &$TrapHandler +} + + +Function Join-Domain { +<# +.SYNOPSIS +Executes "Join domain" action. + +Requires 'CoreFunctions' module +#> + param ( + [String] $DomainName = '', + [String] $UserName = '', + [String] $Password = '', + [String] $OUPath = '', + [Switch] $AllowRestart + ) + begin { + Show-InvocationInfo $MyInvocation + } + end { + Show-InvocationInfo $MyInvocation -End + } + process { + trap { + &$TrapHandler + } + + if ($UserName -eq '') { + $UserName = 'Administrator' + } + + $Credential = New-Credential -UserName "$DomainName\$UserName" -Password $Password + + + if (Test-ComputerName -DomainName $DomainName -ErrorAction 'SilentlyContinue') { + Write-LogWarning "Computer already joined to domain '$DomainName'" + } + else { + Write-Log "Joining computer to domain '$DomainName' ..." + + if ($OUPath -eq '') { + Add-Computer -DomainName $DomainName -Credential $Credential -Force + } + else { + Add-Computer -DomainName $DomainName -Credential $Credential -OUPath $OUPath -Force + } + + $null = Exec 'ipconfig' @('/registerdns') -RedirectStreams + + Write-Log "Waiting 30 seconds to restart ..." + Start-Sleep -Seconds 30 + <# + if ($AllowRestart) { + Write-Log "Restarting computer ..." + Restart-Computer -Force + } + else { + Write-Log "Please restart the computer now." + } + #> + } + } +} diff --git a/meta/io.murano.windows.ActiveDirectory/Resources/scripts/Set-LocalUserPassword.ps1 b/meta/io.murano.windows.ActiveDirectory/Resources/scripts/Set-LocalUserPassword.ps1 new file mode 100644 index 000000000..8708a0f4a --- /dev/null +++ b/meta/io.murano.windows.ActiveDirectory/Resources/scripts/Set-LocalUserPassword.ps1 @@ -0,0 +1,37 @@ + +trap { + &$TrapHandler +} + + +Function Set-LocalUserPassword { + param ( + [String] $UserName, + [String] $Password, + [Switch] $Force + ) + begin { + Show-InvocationInfo $MyInvocation + } + end { + Show-InvocationInfo $MyInvocation -End + } + process { + trap { + &$TrapHandler + } + + if ((Get-WmiObject Win32_UserAccount -Filter "LocalAccount = 'True' AND Name='$UserName'") -eq $null) { + throw "Unable to find local user account '$UserName'" + } + + if ($Force) { + Write-Log "Changing password for user '$UserName' to '*****'" # :) + $null = ([ADSI] "WinNT://./$UserName").SetPassword($Password) + } + else { + Write-LogWarning "You are trying to change password for user '$UserName'. To do this please run the command again with -Force parameter." + } + } +} + diff --git a/meta/packages/activedirectory/UI/ui.yaml b/meta/io.murano.windows.ActiveDirectory/UI/ui.yaml similarity index 100% rename from meta/packages/activedirectory/UI/ui.yaml rename to meta/io.murano.windows.ActiveDirectory/UI/ui.yaml diff --git a/meta/packages/activedirectory/logo2.png b/meta/io.murano.windows.ActiveDirectory/logo2.png similarity index 100% rename from meta/packages/activedirectory/logo2.png rename to meta/io.murano.windows.ActiveDirectory/logo2.png diff --git a/meta/io.murano.windows.ActiveDirectory/manifest.yaml b/meta/io.murano.windows.ActiveDirectory/manifest.yaml new file mode 100644 index 000000000..a5809752a --- /dev/null +++ b/meta/io.murano.windows.ActiveDirectory/manifest.yaml @@ -0,0 +1,27 @@ +Format: 1.0 + +Type: Application + +FullName: io.murano.windows.ActiveDirectory + +Name: Active Directory + +Description: | + A domain service hosted in Windows environment by using Active Directory Role. + May be clustered by combining a number of secondary domain controllers with one primary + +Author: 'murano.io' + +Tags: [Windows, Domain, demo, win2012, microsoft] + +Classes: + io.murano.windows.Host: Host.yaml + io.murano.windows.DomainHost: DomainHost.yaml + io.murano.windows.activeDirectory.ActiveDirectory: ActiveDirectory.yaml + io.murano.windows.activeDirectory.Controller: Controller.yaml + io.murano.windows.activeDirectory.PrimaryController: PrimaryController.yaml + io.murano.windows.activeDirectory.SecondaryController: SecondaryController.yaml + +# UI: ui.yaml # default to ui.yaml, will use default if skipped + +Logo: logo2.png # defaults to logo.png, will use default if skipped \ No newline at end of file diff --git a/meta/packages/core/Classes/application.yaml b/meta/io.murano/Classes/Application.yaml similarity index 100% rename from meta/packages/core/Classes/application.yaml rename to meta/io.murano/Classes/Application.yaml diff --git a/meta/packages/core/Classes/environment.yaml b/meta/io.murano/Classes/Environment.yaml similarity index 100% rename from meta/packages/core/Classes/environment.yaml rename to meta/io.murano/Classes/Environment.yaml diff --git a/meta/packages/core/Classes/object.yaml b/meta/io.murano/Classes/Object.yaml similarity index 100% rename from meta/packages/core/Classes/object.yaml rename to meta/io.murano/Classes/Object.yaml diff --git a/meta/io.murano/Classes/resources/Instance.yaml b/meta/io.murano/Classes/resources/Instance.yaml new file mode 100644 index 000000000..8f702b379 --- /dev/null +++ b/meta/io.murano/Classes/resources/Instance.yaml @@ -0,0 +1,81 @@ +Namespaces: + =: io.murano.resources + sys: io.murano.system + + +Name: Instance + + +Properties: + name: + Contract: $.string().notNull() + flavor: + Contract: $.string().notNull() + image: + Contract: $.string().notNull() + + agent: + Contract: $.class(sys:Agent) + Type: Runtime + + +Workflow: + initialize: + Body: + - $.environment: $.find(Environment).require() + - $.agent: new(sys:Agent, host => $) + - $.resources: new(sys:Resources) + + deploy: + Body: + - $userData: $.prepareUserData() + - $template: + Resources: + $.name: + Type: 'AWS::EC2::Instance' + Properties: + InstanceType: $.flavor + ImageId: $.image + UserData: $userData + - $.environment.stack.updateTemplate($template) + - $.environment.stack.push() + - $.environment.instanceNotifier.trackApplication($this) + + destroy: + Body: + - $template: $.environment.stack.current() + - $patchBlock: + op: remove, + path: format('/Resources/{0}', $.name) + - $template: patch($template, $patchBlock) + - $.environment.stack.setTemplate($template) + - $.environment.stack.push() + - $.environment.instanceNotifier.untrackApplication($this) + + prepareUserData: + Body: + - If: !yaql "'w' in toLower($.image)" + Then: + - $configFile: $.resources.string('Agent-v1.template') + - $initScript: $.resources.string('windows-init.ps1') + Else: + - $configFile: $.resources.string('Agent-v2.template') + - $initScript: $.resources.string('linux-init.sh') + + - $configReplacements: + "%RABBITMQ_HOST%": config(rabbitmq, host) + "%RABBITMQ_PORT%": config(rabbitmq, port) + "%RABBITMQ_USER%": config(rabbitmq, login) + "%RABBITMQ_PASSWORD%": config(rabbitmq, password) + "%RABBITMQ_VHOST%": config(rabbitmq, virtual_host) + "%RABBITMQ_SSL%": str(config(rabbitmq, ssl)).toLower() + "%RABBITMQ_INPUT_QUEUE%": $.agent.queueName() + "%RESULT_QUEUE%": $.environment.agentListener.queueName() + + - $scriptReplacements: + "%AGENT_CONFIG_BASE64%": base64encode($configFile.replace($configReplacements)) + "%INTERNAL_HOSTNAME%": $.name + "%MURANO_SERVER_ADDRESS%": coalesce(config(file_server), config(rabbitmq, host)) + "%CA_ROOT_CERT_BASE64%": "" + + - Return: $initScript.replace($scriptReplacements) \ No newline at end of file diff --git a/meta/io.murano/Resources/Agent-v1.template b/meta/io.murano/Resources/Agent-v1.template new file mode 100644 index 000000000..839abe8ac --- /dev/null +++ b/meta/io.murano/Resources/Agent-v1.template @@ -0,0 +1,36 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/meta/io.murano/Resources/Agent-v2.template b/meta/io.murano/Resources/Agent-v2.template new file mode 100644 index 000000000..b1f3db9d8 --- /dev/null +++ b/meta/io.murano/Resources/Agent-v2.template @@ -0,0 +1,35 @@ +[DEFAULT] +debug=True +verbose=True +log_file = /var/log/murano-agnet.log + +storage=/var/murano/plans + +[rabbitmq] + +# Input queue name +input_queue = %RABBITMQ_INPUT_QUEUE% + +# Output routing key (usually queue name) +result_routing_key = %RESULT_QUEUE% + +# Connection parameters to RabbitMQ service + +# Hostname or IP address where RabbitMQ is located. +host = %RABBITMQ_HOST% + +# RabbitMQ port (5672 is a default) +port = %RABBITMQ_PORT% + +# Use SSL for RabbitMQ connections (True or False) +ssl = %RABBITMQ_SSL% + +# Path to SSL CA certificate or empty to allow self signed server certificate +ca_certs = + +# RabbitMQ credentials. Fresh RabbitMQ installation has "guest" account with "guest" password. +login = %RABBITMQ_USER% +password = %RABBITMQ_PASSWORD% + +# RabbitMQ virtual host (vhost). Fresh RabbitMQ installation has "/" vhost preconfigured. +virtual_host = %RABBITMQ_VHOST% diff --git a/meta/io.murano/Resources/linux-init.sh b/meta/io.murano/Resources/linux-init.sh new file mode 100644 index 000000000..9667f4b7a --- /dev/null +++ b/meta/io.murano/Resources/linux-init.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +service murano-agent stop + +AgentConfigBase64='%AGENT_CONFIG_BASE64%' + +mkdir /etc/murano +echo $AgentConfigBase64 | base64 -d > /etc/murano/agent.conf +chmod 664 /etc/murano/agent.conf + +service murano-agent start diff --git a/meta/io.murano/Resources/windows-init.ps1 b/meta/io.murano/Resources/windows-init.ps1 new file mode 100644 index 000000000..73ef8bbaf --- /dev/null +++ b/meta/io.murano/Resources/windows-init.ps1 @@ -0,0 +1,68 @@ +#ps1 + +$WindowsAgentConfigBase64 = '%AGENT_CONFIG_BASE64%' +$WindowsAgentConfigFile = "C:\Murano\Agent\WindowsAgent.exe.config" +$WindowsAgentLogFile = "C:\Murano\Agent\log.txt" + +$NewComputerName = '%INTERNAL_HOSTNAME%' +$MuranoFileShare = '\\%MURANO_SERVER_ADDRESS%\share' + +$CaRootCertBase64 = "%CA_ROOT_CERT_BASE64%" +$CaRootCertFile = "C:\Murano\ca.cert" + +$RestartRequired = $false + +Import-Module CoreFunctions +Initialize-Logger 'CloudBase-Init' 'C:\Murano\PowerShell.log' + +$ErrorActionPreference = 'Stop' + +trap { + Write-LogError '' + Write-LogError $_ -EntireObject + Write-LogError '' + exit 1 +} + +Write-Log "Importing CA certificate ..." +if ($CaRootCertBase64 -eq '') { + Write-Log "Importing CA certificate ... skipped" +} +else { + ConvertFrom-Base64String -Base64String $CaRootCertBase64 -Path $CaRootCertFile + $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $CaRootCertFile + $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("AuthRoot","LocalMachine") + $store.Open("MaxAllowed") + $store.Add($cert) + $store.Close() + Write-Log "Importing CA certificate ... done" +} + +Write-Log "Updating Murano Windows Agent." +Stop-Service "Murano Agent" +Backup-File $WindowsAgentConfigFile +Remove-Item $WindowsAgentConfigFile -Force -ErrorAction 'SilentlyContinue' +Remove-Item $WindowsAgentLogFile -Force -ErrorAction 'SilentlyContinue' +ConvertFrom-Base64String -Base64String $WindowsAgentConfigBase64 -Path $WindowsAgentConfigFile +Exec sc.exe 'config','"Murano Agent"','start=','delayed-auto' +Write-Log "Service has been updated." + +Write-Log "Adding environment variable 'MuranoFileShare' = '$MuranoFileShare' ..." +[Environment]::SetEnvironmentVariable('MuranoFileShare', $MuranoFileShare, [EnvironmentVariableTarget]::Machine) +Write-Log "Environment variable added." + +Write-Log "Renaming computer to '$NewComputerName' ..." +$null = Rename-Computer -NewName $NewComputerName -Force + +Write-Log "New name assigned, restart required." +$RestartRequired = $true + + +Write-Log 'All done!' +if ( $RestartRequired ) { + Write-Log "Restarting computer ..." + Restart-Computer -Force +} +else { + Start-Service 'Murano Agent' +} diff --git a/meta/io.murano/manifest.yaml b/meta/io.murano/manifest.yaml new file mode 100644 index 000000000..11b771e7d --- /dev/null +++ b/meta/io.murano/manifest.yaml @@ -0,0 +1,21 @@ +Format: 1.0 + +Type: Library + +FullName: io.murano + +Name: Core library + +Description: | + Core MuranoPL library + +Author: 'murano.io' + +Tags: [MuranoPL] + +Classes: + io.murano.Object: Object.yaml + io.murano.Environment: Environment.yaml + io.murano.Application: Application.yaml + + io.murano.resources.Instance: resources/Instance.yaml \ No newline at end of file diff --git a/meta/packages/activedirectory/manifest.yaml b/meta/packages/activedirectory/manifest.yaml deleted file mode 100644 index 573523dd0..000000000 --- a/meta/packages/activedirectory/manifest.yaml +++ /dev/null @@ -1,16 +0,0 @@ -Format: 1.0 -Type: Application -FullName: io.murano.windows.activeDirectory.ActiveDirectory -Name: Active Directory -Description: | - A domain service hosted in Windows environment by using Active Directory Role. - May be clustered by combining a number of secondary domain controllers with one primary -Author: 'murano.io' -Tags: [Windows, Domain, demo, win2012, microsoft] -Classes: - io.murano.windows.activeDirectory.ActiveDirectory: ad.yaml - io.murano.windows.activeDirectory.Controller: controller.yaml - io.murano.windows.activeDirectory.PrimaryController: primary_controller.yaml - io.murano.windows.activeDirectory.SecondaryController: secondary_controller.yaml -# UI: ui.yaml # default to ui.yaml, will use default if skipped -Logo: logo2.png # defaults to logo.png, will use default if skipped \ No newline at end of file diff --git a/meta/packages/core/manifest.yaml b/meta/packages/core/manifest.yaml deleted file mode 100644 index abe16bb68..000000000 --- a/meta/packages/core/manifest.yaml +++ /dev/null @@ -1,12 +0,0 @@ -Format: 1.0 -Type: Library -FullName: io.murano.core -Name: Core library -Description: | - Core MuranoPL library -Author: 'murano.io' -Tags: [MuranoPL] -Classes: - io.murano.Object: object.yaml - io.murano.Environment: environment.yaml - io.murano.Application: application.yaml \ No newline at end of file diff --git a/muranoapi/cmd/manage.py b/muranoapi/cmd/manage.py index 6628802fb..f44edb146 100644 --- a/muranoapi/cmd/manage.py +++ b/muranoapi/cmd/manage.py @@ -92,3 +92,7 @@ def main(): CONF.command.func() except Exception as e: sys.exit("ERROR: %s" % e) + + +if __name__ == '__main__': + main() diff --git a/muranoapi/common/engine.py b/muranoapi/common/engine.py index 0acba0f09..917e069da 100644 --- a/muranoapi/common/engine.py +++ b/muranoapi/common/engine.py @@ -52,7 +52,8 @@ class TaskProcessingEndpoint(object): exc = executor.MuranoDslExecutor(cl, env) obj = exc.load(task['model']) - obj.type.invoke('deploy', exc, obj, {}) + if obj is not None: + obj.type.invoke('deploy', exc, obj, {}) s_res = results_serializer.serialize(obj, exc) rpc.api().process_result(s_res) diff --git a/muranoapi/db/services/environments.py b/muranoapi/db/services/environments.py index 916c6bc0f..a76737f23 100644 --- a/muranoapi/db/services/environments.py +++ b/muranoapi/db/services/environments.py @@ -122,13 +122,15 @@ class EnvironmentServices(object): #preparing data for removal from conductor env = environment.description - env['services'] = {} - env['deleted'] = True + env['Objects'] = None - #Set X-Auth-Token for conductor - env['token'] = token + data = { + 'model': env, + 'token': token, + 'tenant_id': environment.tenant_id + } - rpc.engine().handle_task(env) + rpc.engine().handle_task(data) with unit.begin(): unit.delete(environment) diff --git a/muranoapi/dsl/executor.py b/muranoapi/dsl/executor.py index dd0368b7d..8bb6cd5ca 100644 --- a/muranoapi/dsl/executor.py +++ b/muranoapi/dsl/executor.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import collections import functools import inspect import types @@ -216,5 +217,49 @@ class MuranoDslExecutor(object): if not isinstance(data, types.DictionaryType): raise TypeError() self._attribute_store.load(data.get('Attributes') or []) - return self._object_store.load(data.get('Objects') or {}, - None, self._root_context) + result = self._object_store.load(data.get('Objects') or {}, + None, self._root_context) + self.cleanup(data) + return result + + def cleanup(self, data): + objects_copy = data.get('ObjectsCopy') + if not objects_copy: + return + gc_object_store = object_store.ObjectStore(self._class_loader) + gc_object_store.load(objects_copy, None, self._root_context) + objects_to_clean = [] + for object_id in self._list_potential_object_ids(objects_copy): + if gc_object_store.has(object_id) \ + and not self._object_store.has(object_id): + obj = gc_object_store.get(object_id) + objects_to_clean.append(obj) + if objects_to_clean: + backup = self._object_store + try: + self._object_store = gc_object_store + for obj in objects_to_clean: + methods = obj.type.find_method('destroy') + for cls, method in methods: + try: + cls.invoke(method, self, obj, {}) + except Exception: + pass + finally: + self._object_store = backup + + def _list_potential_object_ids(self, data): + if isinstance(data, types.DictionaryType): + for val in data.values(): + for res in self._list_potential_object_ids(val): + yield res + sys_dict = data.get('?') + if isinstance(sys_dict, types.DictionaryType) \ + and sys_dict.get('id') \ + and sys_dict.get('type'): + yield sys_dict['id'] + elif isinstance(data, collections.Iterable) and not isinstance( + data, types.StringTypes): + for val in data: + for res in self._list_potential_object_ids(val): + yield res diff --git a/muranoapi/dsl/object_store.py b/muranoapi/dsl/object_store.py index 98671ac0d..6970a012f 100644 --- a/muranoapi/dsl/object_store.py +++ b/muranoapi/dsl/object_store.py @@ -39,12 +39,17 @@ class ObjectStore(object): return self._parent_store.get(object_id) return None + def has(self, object_id): + return object_id in self._store + def put(self, murano_object): self._store[murano_object.object_id] = murano_object def load(self, value, parent, context, defaults=None): #tmp_store = ObjectStore(self._class_loader, self) + if value is None: + return None if '?' not in value or 'type' not in value['?']: raise ValueError() system_key = value['?'] diff --git a/muranoapi/dsl/results_serializer.py b/muranoapi/dsl/results_serializer.py index ca7b39622..d8f35592b 100644 --- a/muranoapi/dsl/results_serializer.py +++ b/muranoapi/dsl/results_serializer.py @@ -23,13 +23,19 @@ class ObjRef(object): def serialize(root_object, executor): - serialized_objects = set() - tree = _pass1_serialize(root_object, None, serialized_objects) - _pass2_serialize(tree, serialized_objects) + if root_object is None: + tree = None + attributes = [] + else: + serialized_objects = set() + tree = _pass1_serialize(root_object, None, serialized_objects) + _pass2_serialize(tree, serialized_objects) + attributes = executor.attribute_store.serialize(serialized_objects) return { 'Objects': tree, - 'Attributes': executor.attribute_store.serialize(serialized_objects) + 'ObjectsCopy': tree, + 'Attributes': attributes } diff --git a/muranoapi/engine/system/heat_stack.py b/muranoapi/engine/system/heat_stack.py index 1cc0b439f..7220a699e 100644 --- a/muranoapi/engine/system/heat_stack.py +++ b/muranoapi/engine/system/heat_stack.py @@ -166,21 +166,25 @@ class HeatStack(murano_object.MuranoObject): current_status = self._get_status() if current_status == 'NOT_FOUND': - self._heat_client.stacks.create( - stack_name=self._name, - parameters=self._parameters, - template=self._template, - disable_rollback=False) + if self._template.get('Resources'): + self._heat_client.stacks.create( + stack_name=self._name, + parameters=self._parameters, + template=self._template, + disable_rollback=False) - self._wait_state( - lambda status: status == 'CREATE_COMPLETE') + self._wait_state( + lambda status: status == 'CREATE_COMPLETE') else: - self._heat_client.stacks.update( - stack_id=self._name, - parameters=self._parameters, - template=self._template) - self._wait_state( - lambda status: status == 'UPDATE_COMPLETE') + if self._template.get('Resources'): + self._heat_client.stacks.update( + stack_id=self._name, + parameters=self._parameters, + template=self._template) + self._wait_state( + lambda status: status == 'UPDATE_COMPLETE') + else: + self.delete() self._applied = True @@ -191,3 +195,5 @@ class HeatStack(murano_object.MuranoObject): stack_id=self._name) self._wait_state( lambda status: status in ('DELETE_COMPLETE', 'NOT_FOUND')) + self._template = {} + self._applied = True diff --git a/muranoapi/engine/system/yaql_functions.py b/muranoapi/engine/system/yaql_functions.py index e74410d1c..e74ca8345 100644 --- a/muranoapi/engine/system/yaql_functions.py +++ b/muranoapi/engine/system/yaql_functions.py @@ -17,6 +17,8 @@ import base64 import re import types +import jsonpatch +import jsonpointer import yaql.context import muranoapi.common.config as cfg @@ -188,6 +190,18 @@ def _pselect(collection, composer): return helpers.parallel_select(collection(), composer) +def _patch(obj, patch): + obj = obj() + patch = patch() + if not isinstance(patch, types.ListType): + patch = [patch] + patch = jsonpatch.JsonPatch(patch) + try: + return patch.apply(obj) + except jsonpointer.JsonPointerException: + return obj + + def register(context): context.register_function( lambda json, mappings: _transform_json(json(), mappings()), 'bind') @@ -213,3 +227,4 @@ def register(context): context.register_function(_substr, 'substr') context.register_function(_str, 'str') context.register_function(_int, 'int') + context.register_function(_patch, 'patch') diff --git a/muranoapi/openstack/common/exception.py b/muranoapi/openstack/common/exception.py index d145e804c..db55cd214 100644 --- a/muranoapi/openstack/common/exception.py +++ b/muranoapi/openstack/common/exception.py @@ -124,7 +124,7 @@ class OpenstackException(Exception): if _FATAL_EXCEPTION_FORMAT_ERRORS: raise else: - # at least get the core message out if something happened + # at least get the io.murano message out if something happened self._error_string = self.msg_fmt def __str__(self): diff --git a/requirements.txt b/requirements.txt index 7d3bcb95c..22f196b18 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,6 +18,7 @@ iso8601>=0.1.8 six>=1.5.2 netaddr>=0.7.6 PyYAML>=3.1.0 +jsonpatch>=1.1 # Note you will need gcc buildtools installed and must # have installed libxml headers for lxml to be successfully