~ubuntu-branches/ubuntu/trusty/heat/trusty-security

« back to all changes in this revision

Viewing changes to contrib/rackspace/heat/engine/plugins/clouddatabase.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2013-10-03 09:43:04 UTC
  • mto: This revision was merged to the branch mainline in revision 15.
  • Revision ID: package-import@ubuntu.com-20131003094304-zhhr2brapzlpvjmm
Tags: upstream-2013.2~rc1
ImportĀ upstreamĀ versionĀ 2013.2~rc1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
 
3
#
 
4
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
5
#    not use this file except in compliance with the License. You may obtain
 
6
#    a copy of the License at
 
7
#
 
8
#         http://www.apache.org/licenses/LICENSE-2.0
 
9
#
 
10
#    Unless required by applicable law or agreed to in writing, software
 
11
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
12
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
13
#    License for the specific language governing permissions and limitations
 
14
#    under the License.
 
15
 
 
16
try:
 
17
    from pyrax.exceptions import NotFound
 
18
except ImportError:
 
19
    # fake exception for testing without pyrax
 
20
    class NotFound(Exception):
 
21
        pass
 
22
 
 
23
from heat.common import exception
 
24
from heat.openstack.common import log as logging
 
25
 
 
26
from . import rackspace_resource
 
27
 
 
28
logger = logging.getLogger(__name__)
 
29
 
 
30
 
 
31
class CloudDBInstance(rackspace_resource.RackspaceResource):
 
32
    '''
 
33
    Rackspace cloud database resource.
 
34
    '''
 
35
    database_schema = {
 
36
        "Character_set": {
 
37
            "Type": "String",
 
38
            "Default": "utf8",
 
39
            "Required": False
 
40
        },
 
41
        "Collate": {
 
42
            "Type": "String",
 
43
            "Default": "utf8_general_ci",
 
44
            "Required": False
 
45
        },
 
46
        "Name": {
 
47
            "Type": "String",
 
48
            "Required": True,
 
49
            "MaxLength": "64",
 
50
            "AllowedPattern": "[a-zA-Z0-9_]+[a-zA-Z0-9_@?#\s]*[a-zA-Z0-9_]+"
 
51
        }
 
52
    }
 
53
 
 
54
    user_schema = {
 
55
        "Name": {
 
56
            "Type": "String",
 
57
            "Required": True,
 
58
            "MaxLength": "16",
 
59
            "AllowedPattern": "[a-zA-Z0-9_]+[a-zA-Z0-9_@?#\s]*[a-zA-Z0-9_]+"
 
60
        },
 
61
        "Password": {
 
62
            "Type": "String",
 
63
            "Required": True,
 
64
            "AllowedPattern": "[a-zA-Z0-9_]+[a-zA-Z0-9_@?#\s]*[a-zA-Z0-9_]+"
 
65
        },
 
66
        "Host": {
 
67
            "Type": "String",
 
68
            "Default": "%"
 
69
        },
 
70
        "Databases": {
 
71
            "Type": "List",
 
72
            "Required": True
 
73
        }
 
74
    }
 
75
 
 
76
    properties_schema = {
 
77
        "InstanceName": {
 
78
            "Type": "String",
 
79
            "Required": True,
 
80
            "MaxLength": "255"
 
81
        },
 
82
 
 
83
        "FlavorRef": {
 
84
            "Type": "String",
 
85
            "Required": True
 
86
        },
 
87
 
 
88
        "VolumeSize": {
 
89
            "Type": "Number",
 
90
            "MinValue": 1,
 
91
            "MaxValue": 150,
 
92
            "Required": True
 
93
        },
 
94
 
 
95
        "Databases": {
 
96
            'Type': 'List',
 
97
            'Required': False,
 
98
            'Schema': {
 
99
                'Type': 'Map',
 
100
                'Schema': database_schema
 
101
            }
 
102
        },
 
103
 
 
104
        "Users": {
 
105
            'Type': 'List',
 
106
            'Required': False,
 
107
            'Schema': {
 
108
                'Type': 'Map',
 
109
                'Schema': user_schema
 
110
            }
 
111
        },
 
112
    }
 
113
 
 
114
    attributes_schema = {
 
115
        "hostname": "Hostname of the instance",
 
116
        "href": "Api endpoint reference of the instance"
 
117
    }
 
118
 
 
119
    def __init__(self, name, json_snippet, stack):
 
120
        super(CloudDBInstance, self).__init__(name, json_snippet, stack)
 
121
        self.hostname = None
 
122
        self.href = None
 
123
 
 
124
    def handle_create(self):
 
125
        '''
 
126
        Create Rackspace Cloud DB Instance.
 
127
        '''
 
128
        logger.debug("Cloud DB instance handle_create called")
 
129
        self.sqlinstancename = self.properties['InstanceName']
 
130
        self.flavor = self.properties['FlavorRef']
 
131
        self.volume = self.properties['VolumeSize']
 
132
        self.databases = self.properties.get('Databases', None)
 
133
        self.users = self.properties.get('Users', None)
 
134
 
 
135
        # create db instance
 
136
        logger.info("Creating Cloud DB instance %s" % self.sqlinstancename)
 
137
        instance = self.cloud_db().create(self.sqlinstancename,
 
138
                                          flavor=self.flavor,
 
139
                                          volume=self.volume)
 
140
        if instance is not None:
 
141
            self.resource_id_set(instance.id)
 
142
 
 
143
        self.hostname = instance.hostname
 
144
        self.href = instance.links[0]['href']
 
145
        return instance
 
146
 
 
147
    def check_create_complete(self, instance):
 
148
        '''
 
149
        Check if cloud DB instance creation is complete.
 
150
        '''
 
151
        instance.get()  # get updated attributes
 
152
        if instance.status == 'ERROR':
 
153
            instance.delete()
 
154
            raise exception.Error("Cloud DB instance creation failed.")
 
155
 
 
156
        if instance.status != 'ACTIVE':
 
157
            return False
 
158
 
 
159
        logger.info("Cloud DB instance %s created (flavor:%s, volume:%s)" %
 
160
                    (self.sqlinstancename, self.flavor, self.volume))
 
161
        # create databases
 
162
        for database in self.databases:
 
163
            instance.create_database(
 
164
                database['Name'],
 
165
                character_set=database['Character_set'],
 
166
                collate=database['Collate'])
 
167
            logger.info("Database %s created on cloud DB instance %s" %
 
168
                        (database['Name'], self.sqlinstancename))
 
169
 
 
170
        # add users
 
171
        dbs = []
 
172
        for user in self.users:
 
173
            if user['Databases']:
 
174
                dbs = user['Databases']
 
175
            instance.create_user(user['Name'], user['Password'], dbs)
 
176
            logger.info("Cloud database user %s created successfully" %
 
177
                        (user['Name']))
 
178
        return True
 
179
 
 
180
    def handle_delete(self):
 
181
        '''
 
182
        Delete a Rackspace Cloud DB Instance.
 
183
        '''
 
184
        logger.debug("CloudDBInstance handle_delete called.")
 
185
        if self.resource_id is None:
 
186
            return
 
187
 
 
188
        try:
 
189
            instances = self.cloud_db().delete(self.resource_id)
 
190
        except NotFound:
 
191
            pass
 
192
        self.resource_id = None
 
193
 
 
194
    def validate(self):
 
195
        '''
 
196
        Validate any of the provided params
 
197
        '''
 
198
        res = super(CloudDBInstance, self).validate()
 
199
        if res:
 
200
            return res
 
201
 
 
202
        # check validity of user and databases
 
203
        users = self.properties.get('Users', None)
 
204
        if not users:
 
205
            return
 
206
 
 
207
        databases = self.properties.get('Databases', None)
 
208
        if not databases:
 
209
            return {'Error':
 
210
                    'Databases property is required if Users property'
 
211
                    ' is provided'}
 
212
 
 
213
        for user in users:
 
214
            if not user['Databases']:
 
215
                return {'Error':
 
216
                        'Must provide access to at least one database for '
 
217
                        'user %s' % user['Name']}
 
218
 
 
219
            missing_db = [db_name for db_name in user['Databases']
 
220
                          if db_name not in [db['Name'] for db in databases]]
 
221
            if missing_db:
 
222
                return {'Error':
 
223
                        'Database %s specified for user does not exist in '
 
224
                        'databases.' % missing_db}
 
225
        return
 
226
 
 
227
    def _hostname(self):
 
228
        if self.hostname is None and self.resource_id is not None:
 
229
            dbinstance = self.cloud_db().get(self.resource_id)
 
230
            self.hostname = dbinstance.hostname
 
231
 
 
232
        return self.hostname
 
233
 
 
234
    def _href(self):
 
235
        if self.href is None and self.resource_id is not None:
 
236
            dbinstance = self.cloud_db().get(self.resource_id)
 
237
            self.href = self._gethref(dbinstance)
 
238
 
 
239
        return self.href
 
240
 
 
241
    def _gethref(self, dbinstance):
 
242
        if dbinstance is None or dbinstance.links is None:
 
243
            return None
 
244
 
 
245
        for link in dbinstance.links:
 
246
            if link['rel'] == 'self':
 
247
                return link['href']
 
248
 
 
249
    def _resolve_attribute(self, name):
 
250
        if name == 'hostname':
 
251
            return self._hostname()
 
252
        elif name == 'href':
 
253
            return self._href()
 
254
        else:
 
255
            return None
 
256
 
 
257
 
 
258
# pyrax module is required to work with Rackspace cloud database provider.
 
259
# If it is not installed, don't register clouddatabase provider
 
260
def resource_mapping():
 
261
    if rackspace_resource.PYRAX_INSTALLED:
 
262
        return {
 
263
            'Rackspace::Cloud::DBInstance': CloudDBInstance,
 
264
        }
 
265
    else:
 
266
        return {}