diff --git a/windc/heat_run b/windc/heat_run new file mode 100755 index 0000000..69e4182 --- /dev/null +++ b/windc/heat_run @@ -0,0 +1,5 @@ +#!/bin/bash + +source openrc.sh +heat "$@" + diff --git a/windc/tests/manual/createServiceParameters b/windc/tests/manual/createServiceParameters index 6e9817c..0954f92 100644 --- a/windc/tests/manual/createServiceParameters +++ b/windc/tests/manual/createServiceParameters @@ -4,5 +4,5 @@ "domain": "ACME.cloud", "AdminUser": "Admin", "AdminPassword": "StrongPassword", -"DomainControllerNames": ["APP-AD001","APP-AD002"] +"DomainControllerNames": ["AD-DC001"] } diff --git a/windc/tools/pip-requires b/windc/tools/pip-requires index 0cb916b..70f7e25 100644 --- a/windc/tools/pip-requires +++ b/windc/tools/pip-requires @@ -15,7 +15,7 @@ sqlalchemy-migrate>=0.7.2 httplib2 kombu iso8601>=0.1.4 - +PyChef # For paste.util.template used in keystone.common.template Paste diff --git a/windc/windc/adapters/openstack.py b/windc/windc/adapters/openstack.py new file mode 100644 index 0000000..9ca6733 --- /dev/null +++ b/windc/windc/adapters/openstack.py @@ -0,0 +1,19 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from heatclient import Client + diff --git a/windc/windc/core/builder.py b/windc/windc/core/builder.py index 2dc68e8..15a14e6 100644 --- a/windc/windc/core/builder.py +++ b/windc/windc/core/builder.py @@ -29,5 +29,9 @@ class Builder: def build(self, context, event, data): pass +def create_context(): + context = {} + context['commands']=[] + return context diff --git a/windc/windc/core/builders/ActiveDirectory.py b/windc/windc/core/builders/ActiveDirectory.py index 7181329..703703b 100644 --- a/windc/windc/core/builders/ActiveDirectory.py +++ b/windc/windc/core/builders/ActiveDirectory.py @@ -17,11 +17,14 @@ import logging +import uuid LOG = logging.getLogger(__name__) from windc.core.builder import Builder from windc.core import change_events as events from windc.db import api as db_api +from windc.core.templates import Template +from windc.core import commands as command_api class ActiveDirectory(Builder): def __init__(self): @@ -35,6 +38,8 @@ class ActiveDirectory(Builder): LOG.info ("Got service change event. Analysing..") if self.do_analysis(context, event, dc): self.plan_changes(context, event, dc) + + self.submit_commands(context, event, dc) else: LOG.debug("Not in my scope. Skip event.") pass @@ -44,10 +49,66 @@ class ActiveDirectory(Builder): zones = data['zones'] if data['type'] == self.type and len(zones) == 1: LOG.debug("It is a service which I should build.") + datacenter_id = data['datacenter_id'] + dc = db_api.datacenter_get(context['conf'],data['tenant_id'], + data['datacenter_id']) + datacenter = db_api.unpack_extra(dc) + context['stack_name']=datacenter['name'] return True else: return False def plan_changes(self, context, event, data): + # Here we can plan multiple command execution. + # It might be Heat call command, then chef call command and other + # + LOG.debug("Plan changes...") + self.prepare_template(context, event, data) + self.chef_configuration(context, event, data) + context['commands'].append(self.deploy_template_command(context, event, data)) + context['commands'].append(self.chef_configuration_command(context, event, data)) pass + def prepare_template(self, context, event, data): + LOG.debug("Prepare CloudFormation Template...") + template = Template() + template.add_description('Base template for Active Directory deployment') + sec_grp = template.create_security_group('Security group for AD') + rule = template.create_securitygroup_rule('tcp','3389','3389','0.0.0.0/0') + template.add_rule_to_securitygroup(sec_grp, rule) + template.add_resource('ADSecurityGroup', sec_grp) + + instance = template.create_instance() + instance_name= 'AD-DC001' + template.add_security_group(instance, 'ADSecurityGroup') + template.add_resource(instance_name, instance) + + template.add_output_value(instance_name+'-IP',{"Fn::GetAtt" : [instance_name,'PublicIp']}, + 'Public IP for the domain controller.') + context['template']=template + pass + + def deploy_template_command(self, context, event, data): + LOG.debug("Creating CloudFormation Template deployment command...") + fname = "templates/"+str(uuid.uuid4()) + f=open(fname, "w") + f.write(context['template'].to_json()) + f.close() + context['template_name']=fname + command = command_api.Command(command_api.TEMPLATE_DEPLOYMENT_COMMAND, context) + return command + pass + + def chef_configuration(self, context, event, data): + LOG.debug("Creating Chef configuration...") + context['Role'] = 'pdc' + pass + + def chef_configuration_command(self, context, event, data): + LOG.debug("Creating Chef configuration command...") + command = command_api.Command(command_api.CHEF_COMMAND, context) + return command + + def submit_commands(self, context, event, data): + LOG.debug("Submit commands for execution...") + pass \ No newline at end of file diff --git a/windc/windc/core/change_events.py b/windc/windc/core/change_events.py index 8324a7f..8955b58 100644 --- a/windc/windc/core/change_events.py +++ b/windc/windc/core/change_events.py @@ -20,6 +20,8 @@ import logging LOG = logging.getLogger(__name__) from windc.core import builder_set +from windc.core import builder +from windc.drivers import command_executor #Declare events types SCOPE_SERVICE_CHANGE = "Service" @@ -40,11 +42,14 @@ class Event: def change_event(conf, event, data): LOG.info("Change event of type: %s ", event) - context = {} + context = builder.create_context() context['conf'] = conf for builder_type in builder_set.builders.set: - builder = builder_set.builders.set[builder_type] - builder.build(context, event, data) + builder_instance = builder_set.builders.set[builder_type] + builder_instance.build(context, event, data) + + executor = command_executor.Executor() + executor.execute(context['commands']) pass diff --git a/windc/windc/core/commands.py b/windc/windc/core/commands.py new file mode 100644 index 0000000..089ec27 --- /dev/null +++ b/windc/windc/core/commands.py @@ -0,0 +1,33 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +TEMPLATE_DEPLOYMENT_COMMAND = "Template" +CHEF_COMMAND = "Chef" + +class Command: + type = "Empty" + context = None + + def __init__(self): + self.type = "Empty" + self.context = None + + def __init__(self, type, context): + self.type = type + self.context = context + + diff --git a/windc/windc/core/templates.py b/windc/windc/core/templates.py new file mode 100644 index 0000000..47a7c90 --- /dev/null +++ b/windc/windc/core/templates.py @@ -0,0 +1,107 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import logging +from windc.common.wsgi import JSONResponseSerializer +LOG = logging.getLogger(__name__) + +class Template: + def __init__(self): + self.content = {'AWSTemplateFormatVersion':'2010-09-09', 'Description':'', + 'Parameters':{}} + self.content['Mappings'] = { + "AWSInstanceType2Arch" : { + "t1.micro" : { "Arch" : "32" }, + "m1.small" : { "Arch" : "32" }, + "m1.large" : { "Arch" : "64" }, + "m1.xlarge" : { "Arch" : "64" }, + "m2.xlarge" : { "Arch" : "64" }, + "m2.2xlarge" : { "Arch" : "64" }, + "m2.4xlarge" : { "Arch" : "64" }, + "c1.medium" : { "Arch" : "32" }, + "c1.xlarge" : { "Arch" : "64" }, + "cc1.4xlarge" : { "Arch" : "64" } + }, + "DistroArch2AMI": { + "F16" : { "32" : "F16-i386-cfntools", "64" : "F16-x86_64-cfntools" }, + "F17" : { "32" : "F17-i386-cfntools", "64" : "F17-x86_64-cfntools" }, + "U10" : { "32" : "U10-i386-cfntools", "64" : "U10-x86_64-cfntools" }, + "RHEL-6.1": { "32" : "rhel61-i386-cfntools", "64" : "rhel61-x86_64-cfntools" }, + "RHEL-6.2": { "32" : "rhel62-i386-cfntools", "64" : "rhel62-x86_64-cfntools" }, + "RHEL-6.3": { "32" : "rhel63-i386-cfntools", "64" : "rhel63-x86_64-cfntools" } + } + } + self.content['Resources'] = {} + self.content['Outputs'] = {} + + def to_json(self): + serializer = JSONResponseSerializer() + json = serializer.to_json(self.content) + return json + + + def empty_template(self): + pass + + def add_description(self, description): + self.content['Description'] = description + + def add_parameter(self, name, parameter): + self.content['Parameters'].update({name : parameter}) + + def add_resource(self, name, resource): + self.content['Resources'].update({name : resource}) + + def create_parameter(self, defult, type, decription): + parameter = {'Default':default, 'Type':type, 'Description':description} + return parameter + + def create_security_group(self, description): + sec_grp = {'Type':'AWS::EC2::SecurityGroup'} + sec_grp['Properties'] = {} + sec_grp['Properties']['GroupDescription'] = description + sec_grp['Properties']['SecurityGroupIngress'] = [] + return sec_grp + + def add_rule_to_securitygroup(self, grp, rule): + grp['Properties']['SecurityGroupIngress'].append(rule) + + def create_securitygroup_rule(self, proto, f_port, t_port, cidr): + rule = {'IpProtocol':proto, 'FromPort':f_port, 'ToPort':t_port,'CidrIp': cidr} + return rule + + def create_instance(self): + instance = {'Type':'AWS::EC2::Instance','Metadata':{},'Properties':{}} + instance['Properties']['ImageId'] = 'U10-x86_64-cfntools' + instance['Properties']['SecurityGroups']=[] + instance['Properties']['KeyName'] = 'keero-linux-keys' + instance['Properties']['InstanceType'] = 'm1.small' + return instance + + def add_security_group(self, instance, grp_name): + instance['Properties']['SecurityGroups'].append({'Ref': grp_name}) + + def add_output_value(self, name, value, description): + self.content['Outputs'].update({name:{'Value':value, 'Description':description}}) + + def get_content(self): + return self.content + + + + diff --git a/windc/windc/drivers/command_executor.py b/windc/windc/drivers/command_executor.py new file mode 100644 index 0000000..c7c0d2f --- /dev/null +++ b/windc/windc/drivers/command_executor.py @@ -0,0 +1,37 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from windc.core import commands as commands_api +from windc.drivers import openstack_heat + +class Executor: + + map = {commands_api.TEMPLATE_DEPLOYMENT_COMMAND : openstack_heat.Heat} + + def __init__(self): + pass + + def execute(self, commands): + for command in commands: + if command.type == commands_api.TEMPLATE_DEPLOYMENT_COMMAND: + executor = openstack_heat.Heat() + executor.execute(command) + + diff --git a/windc/windc/drivers/openstack_heat.py b/windc/windc/drivers/openstack_heat.py new file mode 100644 index 0000000..7661bb6 --- /dev/null +++ b/windc/windc/drivers/openstack_heat.py @@ -0,0 +1,38 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +#from heatclient import Client +from subprocess import call + +import logging +LOG = logging.getLogger(__name__) + +class Heat: + + def __init__(self): + pass + + def execute(self, command): +# client = Client('1',OS_IMAGE_ENDPOINT, OS_TENANT_ID) + LOG.debug('Calling heat script to execute template') + call(["./heat_run","stack-create","-f "+command.context['template_name'], + command.context['stack_name']]) + pass +