Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 122 additions & 10 deletions pyrax/clouddatabases.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from pyrax.manager import BaseManager
from pyrax.resource import BaseResource
import pyrax.utils as utils
from six.moves import urllib


def assure_instance(fnc):
Expand All @@ -36,6 +37,19 @@ def _wrapped(self, instance, *args, **kwargs):
return fnc(self, instance, *args, **kwargs)
return _wrapped

def check_type_and_version(body, type, version):
"""
Makes sure that for database creation statements the database
type and version are specified and then inserts them into the
datastore parameter
"""
if type is not None or version is not None:
required = (type, version)
if all(required):
body['datastore'] = {"type": type, "version": version}
else:
raise exc.MissingCloudDatabaseParameter("Specifying a datastore"
" requires both the datastore type as well as the version.")


class CloudDatabaseVolume(object):
Expand Down Expand Up @@ -64,7 +78,6 @@ def get(self, att):
return getattr(self, att)



class CloudDatabaseManager(BaseManager):
"""
This class manages communication with Cloud Database instances.
Expand Down Expand Up @@ -103,15 +116,9 @@ def _create_body(self, name, flavor=None, volume=None, databases=None,
"users": users,
}}

if type is not None or version is not None:
required = (type, version)
if all(required):
body['instance']['datastore'] = {"type": type, "version": version}
else:
raise exc.MissingCloudDatabaseParameter("Specifying a datastore"
" requires both the datastore type as well as the version.")
return body
check_type_and_version(body['instance'], type, version)

return body

def create_backup(self, instance, name, description=None):
"""
Expand Down Expand Up @@ -337,6 +344,94 @@ def list(self, instance=None, limit=20, marker=0):
return self.api._manager._list_backups_for_instance(instance, limit=limit,
marker=marker)

class CloudDatabaseHAInstanceACL(BaseResource):
"""
This class represents an HA MySQL ACL
"""
def __init__(self, *args, **kwargs):
super(CloudDatabaseHAInstanceACL, self).__init__(*args, **kwargs)

def delete(self):
"""This class doesn't have an 'id', so pass the address."""
self.manager.delete(urllib.parse.quote(self.address, safe=''))


class CloudDatabaseHAInstanceACLManager(BaseManager):
"""
This class manages ACLs for HA instances
"""
def _create_body(self, name, address):
body = {"address": address}

return body


class CloudDatabaseHAInstance(BaseResource):
"""
This class represents an HA MySQL instance in the cloud.
"""
def __init__(self, *args, **kwargs):
super(CloudDatabaseHAInstance, self).__init__(*args, **kwargs)

self._acl_manager = CloudDatabaseHAInstanceACLManager(self.manager.api,
resource_class=CloudDatabaseHAInstanceACL,
response_key="acl",
uri_base="ha/%s/acls" % self.id)

def add_acl(self, address):
self._acl_manager.create(None, address, return_none=True)

def list_acls(self):
return self._acl_manager.list()


class CloudDatabaseSpec(object):
"""
This class holds information on the type of cloud database to be created
during an HA create operation
"""
def __init__(self, name, volume, flavor):
self.name = name
self.volume = volume
self.flavor = flavor


class CloudDatabaseHAManager(BaseManager):
def _create_body(self, name, type, version, replica_source, replicas):
"""
Creates a new highly available database instance
"""
source_flavor_ref = self.api._get_flavor_ref(replica_source.flavor)

body = {"ha": {
"name": name,
"replica_source": [
{
"volume": {
"size": replica_source.volume
},
"flavorRef": source_flavor_ref,
"name": replica_source.name
}
],
"replicas": []
}}

for replica in replicas:
replica_flavor_ref = self.api._get_flavor_ref(replica.flavor)
body['ha']['replicas'].append(
{
"volume": {
"size": replica.volume
},
"flavorRef": replica_flavor_ref,
"name": replica.name
}
)

check_type_and_version(body['ha'], type, version)

return body


class CloudDatabaseInstance(BaseResource):
Expand Down Expand Up @@ -672,7 +767,6 @@ class CloudDatabaseBackup(BaseResource):
_non_display = ["locationRef"]



class CloudDatabaseClient(BaseClient):
"""
This is the primary class for interacting with Cloud Databases.
Expand All @@ -693,6 +787,9 @@ def _configure_manager(self):
self._backup_manager = CloudDatabaseBackupManager(self,
resource_class=CloudDatabaseBackup, response_key="backup",
uri_base="backups")
self._ha_manager = CloudDatabaseHAManager(self,
resource_class=CloudDatabaseHAInstance, response_key="ha_instance",
uri_base="ha")


@assure_instance
Expand Down Expand Up @@ -925,3 +1022,18 @@ def restore_backup(self, backup, name, flavor, volume):
instance, as well as a flavor and size (in GB) for the instance.
"""
return self._manager.restore_backup(backup, name, flavor, volume)

def create_ha(self, name, type, version, replica_source, replicas):
"""
Creates a new highly available database instance. The name of the HA
instance, the database type and version, a replica source, and a
list of replicas must be specified.
"""
return self._ha_manager.create(name, type, version, replica_source,
replicas)

def list_ha(self):
"""
Lists all high availability instances
"""
return self._ha_manager.list()
26 changes: 26 additions & 0 deletions tests/unit/test_cloud_databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from pyrax.clouddatabases import CloudDatabaseUser
from pyrax.clouddatabases import CloudDatabaseVolume
from pyrax.clouddatabases import assure_instance
from pyrax.clouddatabases import CloudDatabaseSpec
import pyrax.exceptions as exc
from pyrax.resource import BaseResource
import pyrax.utils as utils
Expand Down Expand Up @@ -921,6 +922,31 @@ def test_create_body_datastore(self):
"datastore": {"type": "MariaDB", "version": "10"}}}
self.assertEqual(ret, expected)

@patch("pyrax.manager.BaseManager", new=fakes.FakeManager)
def test_list_ha(self):
clt = self.client
clt._ha_manager.list = Mock()
limit = utils.random_unicode()
marker = utils.random_unicode()
clt.list_ha()
clt._ha_manager.list.assert_called_once_with()

@patch("pyrax.manager.BaseManager", new=fakes.FakeManager)
def test_create_ha(self):
clt = self.client
clt._ha_manager.create = Mock()

replicasrc = CloudDatabaseSpec(name="src", volume=1, flavor=2)
replica1 = CloudDatabaseSpec(name="rep", volume=1, flavor=2)
replicas = [replica1]

#name, type, version, replica_source, replicas

db = clt.create_ha(name="test", type="mysql", version="1.0",
replica_source=replicasrc, replicas=replicas)

clt._ha_manager.create.assert_called_once_with("test",
"mysql", "1.0", replicasrc, replicas)

if __name__ == "__main__":
unittest.main()