Designate/Blueprints/Bulk Actions

Overview
A user should be able to create, modify, and delete multiple resources.

There are many use cases for applying multiple operations at once. For example:
 * Zone level TTLs for multiple zones may need updating for a migration.
 * Account/tenant cleanup is underwhttps://wiki.openstack.org/w/index.php?title=Designate/Blueprints/Bulk_Actions&action=edit&section=1ay and zones need to be purged.
 * A new user adding all of their zones quickly.

Phase 1: Making the Database Interaction More Transactional
The database interactions with storage should work in the following way:


 * 1) Begin the transaction
 * 2) Execute the appropriate data manipulations to accomplish the task at hand (ie Create a Zone)
 * 3) If no errors occur then commit the transaction and end it
 * 4) If errors occur then rollback the transaction and end it

SQLAlchemy supports transactions via sessions so the SQLAlchemy driver will need to be updated to use these operations.

That should consist of:

'''   def begin(self): self.session.begin(subtransactions=True)

def commit(self): self.session.commit

def rollback(self): self.session.rollback

To be called in designate/storage/api.py using "self.storage.begin". Subtransactions must be used in begin because the session itself is already part of a transaction.

This will allow the elimination of the manual rollbacks in the storage/api.py by utilizing the built-in rollback capabilities in SQLAlchemy. For example:

'''   @contextlib.contextmanager def create_domain(self, context, values): domain = self.storage.create_domain(context, values) try: yield domain except Exception: with excutils.save_and_reraise_exception: self.storage.delete_domain(context, domain['id'])

Will change to:

'''   @contextlib.contextmanager def create_domain(self, context, values): self.storage.begin domain = self.storage.create_domain(context, values) try: yield domain except Exception: with excutils.save_and_reraise_exception: self.storage.rollback else: self.storage.commit

Phase 2: Supporting Bulk Actions
Note: This needs to be fleshed out more and discussed.

There are a couple of different ways this could be done.

Here are a few of different ways the API calls could look. There are pretty much endless possiblities in this regard that will have to be discussed.

''' http://xx.xx.x.x:9001/v2/zones     POST {  "zone": { "name": "example.org.", "email": "joe@example.org", "ttl": 7200 },  "zone": { "name": "example2.org.", "email": "joe@example.org", "ttl": 7200 } } OR { "zones": { "email": "joe@example.org", "ttl": 7200, "names": [ {               "name": "example.org." },           {                "name": "example2.org." }       ]    } }  OR { example.org example2.org }

Some of the operations will no doubt have their own blueprints and all that, but these two options are just two schools of thought on how bulk actions could be accomplished.

Option 1
Expand the new transactions to be called one at a time.

So whatever the implementation of the API call, the methods for bulk actions would call the ones for the single actions one at a time, and report the actions back. This is essentially equivalent to making a script that would perform the bulk action you require.

The option would allow for some of the requests to succeed and some to fail. This would be reported to the user. It would then be their prerogative to fix whatever issue caused them to fail, or try again if it was Designates fault.

This method is essentially implemented by writing a script that utilizes the client or the API.

Option 2
The "all or nothing" approach.

A bulk action will attempt to be applied, but if any part of it fails, the entire action is invalid and everything is rolled back.

On a failure, an error message would be passed back to the user telling them what exactly failed, and they could retry or reconsider.

This option prevents users from getting to a state they haven't desired. A user would never try to add 100 zone and end up with 77 added in this scenario. It minimizes risk of getting into a situation that changes many things, but doesn't accomplish the original intention of the user should some things fail.