Browse Source

Support to create public trove instance

- The users need to specify the network to create Trove instance, but
  trove-taskmanager will create port in that network for Nova instance
  creation. Using port gives Trove more capabilities to define how the
  database service is exposed.
- Deprecate ICMP protocol for the instance.
- Restrict 'nics' parameter for creating instance.
- Add 'access' parameter for creating instance.
- Add 'public_network_id' option in order to create floating IP for the
  instance.
- Do not create records for security groups, but Trove can still delete
  existing instances for backward compatibility.
- Delete unreasonable Host, Account, Storage API.

Story: 2006500
Task: 36468
Task: 36466
Change-Id: I80827e1ad5e6b130cbf94c2bb7a909c44d5cf1e5
changes/74/680374/8
Lingxian Kong 2 weeks ago
parent
commit
c33fa67066
81 changed files with 859 additions and 2443 deletions
  1. 15
    15
      devstack/plugin.sh
  2. 1
    0
      etc/tests/localhost.test.conf
  3. 2
    6
      etc/trove/trove.conf.test
  4. 0
    3
      run_tests.py
  5. 0
    1
      setup.cfg
  6. 8
    8
      trove/backup/models.py
  7. 4
    2
      trove/cluster/service.py
  8. 22
    1
      trove/common/apischema.py
  9. 42
    13
      trove/common/cfg.py
  10. 4
    17
      trove/common/exception.py
  11. 1
    1
      trove/common/glance_remote.py
  12. 1
    1
      trove/common/limits.py
  13. 100
    0
      trove/common/neutron.py
  14. 1
    1
      trove/common/notification.py
  15. 1
    1
      trove/common/policy.py
  16. 4
    4
      trove/common/remote.py
  17. 10
    5
      trove/common/server_group.py
  18. 2
    2
      trove/common/strategies/cluster/experimental/cassandra/api.py
  19. 2
    2
      trove/common/strategies/cluster/experimental/galera_common/api.py
  20. 4
    4
      trove/common/strategies/cluster/experimental/mongodb/api.py
  21. 2
    2
      trove/common/strategies/cluster/experimental/redis/api.py
  22. 2
    2
      trove/common/strategies/cluster/experimental/vertica/api.py
  23. 2
    2
      trove/common/trove_remote.py
  24. 7
    4
      trove/common/utils.py
  25. 1
    1
      trove/common/views.py
  26. 3
    3
      trove/configuration/models.py
  27. 3
    3
      trove/configuration/service.py
  28. 2
    2
      trove/db/models.py
  29. 0
    0
      trove/extensions/account/__init__.py
  30. 0
    58
      trove/extensions/account/models.py
  31. 0
    48
      trove/extensions/account/service.py
  32. 0
    37
      trove/extensions/account/views.py
  33. 0
    0
      trove/extensions/mgmt/host/__init__.py
  34. 0
    0
      trove/extensions/mgmt/host/instance/__init__.py
  35. 0
    60
      trove/extensions/mgmt/host/instance/service.py
  36. 0
    102
      trove/extensions/mgmt/host/models.py
  37. 0
    47
      trove/extensions/mgmt/host/service.py
  38. 0
    51
      trove/extensions/mgmt/host/views.py
  39. 0
    0
      trove/extensions/mgmt/volume/__init__.py
  40. 0
    50
      trove/extensions/mgmt/volume/models.py
  41. 0
    39
      trove/extensions/mgmt/volume/service.py
  42. 0
    40
      trove/extensions/mgmt/volume/views.py
  43. 0
    44
      trove/extensions/routes/account.py
  44. 0
    23
      trove/extensions/routes/mgmt.py
  45. 4
    94
      trove/extensions/security_group/models.py
  46. 1
    1
      trove/guestagent/pkg.py
  47. 17
    13
      trove/instance/models.py
  48. 8
    4
      trove/instance/service.py
  49. 1
    1
      trove/instance/tasks.py
  50. 10
    7
      trove/module/models.py
  51. 0
    14
      trove/network/base.py
  52. 0
    91
      trove/network/neutron.py
  53. 0
    25
      trove/network/nova.py
  54. 2
    2
      trove/taskmanager/api.py
  55. 23
    13
      trove/taskmanager/manager.py
  56. 197
    160
      trove/taskmanager/models.py
  57. 46
    110
      trove/tests/api/instances.py
  58. 0
    207
      trove/tests/api/mgmt/accounts.py
  59. 0
    20
      trove/tests/api/mgmt/admin_required.py
  60. 0
    214
      trove/tests/api/mgmt/hosts.py
  61. 9
    1
      trove/tests/api/mgmt/instances.py
  62. 7
    1
      trove/tests/api/mgmt/malformed_json.py
  63. 0
    115
      trove/tests/api/mgmt/storage.py
  64. 99
    34
      trove/tests/api/root.py
  65. 67
    0
      trove/tests/fakes/neutron.py
  66. 2
    8
      trove/tests/fakes/nova.py
  67. 0
    6
      trove/tests/int_tests.py
  68. 3
    1
      trove/tests/root_logger.py
  69. 0
    3
      trove/tests/unittests/api/common/test_extensions.py
  70. 13
    13
      trove/tests/unittests/backup/test_backup_models.py
  71. 5
    2
      trove/tests/unittests/common/test_policy.py
  72. 0
    0
      trove/tests/unittests/network/__init__.py
  73. 0
    132
      trove/tests/unittests/network/test_neutron_driver.py
  74. 0
    0
      trove/tests/unittests/secgroups/__init__.py
  75. 0
    182
      trove/tests/unittests/secgroups/test_security_group.py
  76. 4
    3
      trove/tests/unittests/taskmanager/test_api.py
  77. 3
    1
      trove/tests/unittests/taskmanager/test_manager.py
  78. 92
    163
      trove/tests/unittests/taskmanager/test_models.py
  79. 0
    88
      trove/tests/unittests/trove_testtools.py
  80. 0
    1
      trove/tests/util/__init__.py
  81. 0
    8
      trove/tests/util/users.py

+ 15
- 15
devstack/plugin.sh View File

@@ -240,18 +240,18 @@ function configure_trove {
240 240
     iniset $TROVE_CONF DEFAULT remote_neutron_client trove.common.single_tenant_remote.neutron_client_trove_admin
241 241
 
242 242
     iniset $TROVE_CONF DEFAULT default_datastore $TROVE_DATASTORE_TYPE
243
-    iniset $TROVE_CONF cassandra tcp_ports 22,7000,7001,7199,9042,9160
244
-    iniset $TROVE_CONF couchbase tcp_ports 22,8091,8092,4369,11209-11211,21100-21199
245
-    iniset $TROVE_CONF couchdb tcp_ports 22,5984
246
-    iniset $TROVE_CONF db2 tcp_ports 22,50000
247
-    iniset $TROVE_CONF mariadb tcp_ports 22,3306,4444,4567,4568
248
-    iniset $TROVE_CONF mongodb tcp_ports 22,2500,27017,27019
249
-    iniset $TROVE_CONF mysql tcp_ports 22,3306
250
-    iniset $TROVE_CONF percona tcp_ports 22,3306
251
-    iniset $TROVE_CONF postgresql tcp_ports 22,5432
252
-    iniset $TROVE_CONF pxc tcp_ports 22,3306,4444,4567,4568
253
-    iniset $TROVE_CONF redis tcp_ports 22,6379,16379
254
-    iniset $TROVE_CONF vertica tcp_ports 22,5433,5434,5444,5450,4803
243
+    iniset $TROVE_CONF cassandra tcp_ports 7000,7001,7199,9042,9160
244
+    iniset $TROVE_CONF couchbase tcp_ports 8091,8092,4369,11209-11211,21100-21199
245
+    iniset $TROVE_CONF couchdb tcp_ports 5984
246
+    iniset $TROVE_CONF db2 tcp_ports 50000
247
+    iniset $TROVE_CONF mariadb tcp_ports 3306,4444,4567,4568
248
+    iniset $TROVE_CONF mongodb tcp_ports 2500,27017,27019
249
+    iniset $TROVE_CONF mysql tcp_ports 3306
250
+    iniset $TROVE_CONF percona tcp_ports 3306
251
+    iniset $TROVE_CONF postgresql tcp_ports 5432
252
+    iniset $TROVE_CONF pxc tcp_ports 3306,4444,4567,4568
253
+    iniset $TROVE_CONF redis tcp_ports 6379,16379
254
+    iniset $TROVE_CONF vertica tcp_ports 5433,5434,5444,5450,4803
255 255
 
256 256
     # configure apache related files
257 257
     if [[ "${TROVE_USE_MOD_WSGI}" == "TRUE" ]]; then
@@ -457,10 +457,10 @@ function start_trove {
457 457
         enable_apache_site trove-api
458 458
         restart_apache_server
459 459
     else
460
-        run_process tr-api "$TROVE_BIN_DIR/trove-api --config-file=$TROVE_CONF --debug"
460
+        run_process tr-api "$TROVE_BIN_DIR/trove-api --config-file=$TROVE_CONF"
461 461
     fi
462
-    run_process tr-tmgr "$TROVE_BIN_DIR/trove-taskmanager --config-file=$TROVE_CONF --debug"
463
-    run_process tr-cond "$TROVE_BIN_DIR/trove-conductor --config-file=$TROVE_CONF --debug"
462
+    run_process tr-tmgr "$TROVE_BIN_DIR/trove-taskmanager --config-file=$TROVE_CONF"
463
+    run_process tr-cond "$TROVE_BIN_DIR/trove-conductor --config-file=$TROVE_CONF"
464 464
 }
465 465
 
466 466
 # stop_trove() - Stop running processes

+ 1
- 0
etc/tests/localhost.test.conf View File

@@ -18,6 +18,7 @@
18 18
 
19 19
     "nova_client": null,
20 20
 
21
+    "shared_network": "b19b5da0-d2f6-11e9-9382-00224d6b7bc1",
21 22
 
22 23
     "users": [
23 24
         {

+ 2
- 6
etc/trove/trove.conf.test View File

@@ -5,6 +5,7 @@ remote_nova_client = trove.tests.fakes.nova.fake_create_nova_client
5 5
 remote_guest_client = trove.tests.fakes.guestagent.fake_create_guest_client
6 6
 remote_swift_client = trove.tests.fakes.swift.fake_create_swift_client
7 7
 remote_cinder_client = trove.tests.fakes.nova.fake_create_cinder_client
8
+remote_neutron_client = trove.tests.fakes.neutron.fake_create_neutron_client
8 9
 
9 10
 # Fake out the RPC implementation
10 11
 transport_url = 'fake:/'
@@ -17,20 +18,15 @@ trove_dns_support = True
17 18
 dns_driver = trove.tests.fakes.dns.FakeDnsDriver
18 19
 dns_instance_entry_factory = trove.tests.fakes.dns.FakeDnsInstanceEntryFactory
19 20
 
20
-
21 21
 # This will remove some of the verbose logging when trying to diagnose tox issues
22 22
 default_log_levels=routes.middleware=ERROR,trove.common.auth=WARN
23 23
 
24 24
 log_file = trovetest.log
25
-
26 25
 use_stderr = False
27
-
28
-# Show debugging output in logs (sets DEBUG log level output)
29 26
 debug = True
30 27
 
31 28
 # Address to bind the API server
32 29
 bind_host = 0.0.0.0
33
-
34 30
 # Port the bind the API server to
35 31
 bind_port = 8779
36 32
 
@@ -49,7 +45,6 @@ nova_proxy_admin_user = admin
49 45
 nova_proxy_admin_pass = 3de4922d8b6ac5a1aad9
50 46
 nova_proxy_admin_tenant_id =
51 47
 trove_auth_url = http://0.0.0.0/identity/v2.0
52
-
53 48
 os_region_name = RegionOne
54 49
 nova_compute_service_type = compute
55 50
 nova_service_name = Compute Service
@@ -105,6 +100,7 @@ control_exchange = trove
105 100
 paste_config_file=api-paste.ini.test
106 101
 
107 102
 [mysql]
103
+root_on_create = False
108 104
 volume_support = True
109 105
 device_path = /dev/vdb
110 106
 

+ 0
- 3
run_tests.py View File

@@ -213,13 +213,10 @@ def import_tests():
213 213
     from trove.tests.api import instances_mysql_down  # noqa
214 214
     from trove.tests.api import instances_resize  # noqa
215 215
     from trove.tests.api import limits  # noqa
216
-    from trove.tests.api.mgmt import accounts  # noqa
217 216
     from trove.tests.api.mgmt import admin_required  # noqa
218
-    from trove.tests.api.mgmt import hosts  # noqa
219 217
     from trove.tests.api.mgmt import instances as mgmt_instances  # noqa
220 218
     from trove.tests.api.mgmt import instances_actions as mgmt_actions  # noqa
221 219
     from trove.tests.api.mgmt import malformed_json  # noqa
222
-    from trove.tests.api.mgmt import storage  # noqa
223 220
     from trove.tests.api import replication  # noqa
224 221
     from trove.tests.api import root  # noqa
225 222
     from trove.tests.api import root_on_create  # noqa

+ 0
- 1
setup.cfg View File

@@ -38,7 +38,6 @@ console_scripts =
38 38
     trove-status = trove.cmd.status:main
39 39
 
40 40
 trove.api.extensions =
41
-    account = trove.extensions.routes.account:Account
42 41
     mgmt = trove.extensions.routes.mgmt:Mgmt
43 42
     mysql = trove.extensions.routes.mysql:Mysql
44 43
 

+ 8
- 8
trove/backup/models.py View File

@@ -100,7 +100,7 @@ class Backup(object):
100 100
             try:
101 101
                 db_info = DBBackup.create(name=name,
102 102
                                           description=description,
103
-                                          tenant_id=context.tenant,
103
+                                          tenant_id=context.project_id,
104 104
                                           state=BackupState.NEW,
105 105
                                           instance_id=instance_id,
106 106
                                           parent_id=parent_id or
@@ -124,7 +124,7 @@ class Backup(object):
124 124
                            }
125 125
             api.API(context).create_backup(backup_info, instance_id)
126 126
             return db_info
127
-        return run_with_quotas(context.tenant,
127
+        return run_with_quotas(context.project_id,
128 128
                                {'backups': 1},
129 129
                                _create_resources)
130 130
 
@@ -188,7 +188,7 @@ class Backup(object):
188 188
         filters = [DBBackup.deleted == 0]
189 189
 
190 190
         if not all_projects:
191
-            filters.append(DBBackup.tenant_id == context.tenant)
191
+            filters.append(DBBackup.tenant_id == context.project_id)
192 192
         if instance_id:
193 193
             filters.append(DBBackup.instance_id == instance_id)
194 194
 
@@ -215,7 +215,7 @@ class Backup(object):
215 215
                                     deleted=False)
216 216
         else:
217 217
             query = query.filter_by(instance_id=instance_id,
218
-                                    tenant_id=context.tenant,
218
+                                    tenant_id=context.project_id,
219 219
                                     deleted=False)
220 220
         return cls._paginate(context, query)
221 221
 
@@ -278,7 +278,7 @@ class Backup(object):
278 278
             cls.verify_swift_auth_token(context)
279 279
             api.API(context).delete_backup(backup_id)
280 280
 
281
-        return run_with_quotas(context.tenant,
281
+        return run_with_quotas(context.project_id,
282 282
                                {'backups': -1},
283 283
                                _delete_resources)
284 284
 
@@ -288,9 +288,9 @@ class Backup(object):
288 288
             client = create_swift_client(context)
289 289
             client.get_account()
290 290
         except ClientException:
291
-            raise exception.SwiftAuthError(tenant_id=context.tenant)
291
+            raise exception.SwiftAuthError(tenant_id=context.project_id)
292 292
         except exception.NoServiceEndpoint:
293
-            raise exception.SwiftNotFound(tenant_id=context.tenant)
293
+            raise exception.SwiftNotFound(tenant_id=context.project_id)
294 294
         except ConnectionError:
295 295
             raise exception.SwiftConnectionError()
296 296
 
@@ -365,4 +365,4 @@ class DBBackup(DatabaseModelBase):
365 365
             if e.http_status == 404:
366 366
                 return False
367 367
             else:
368
-                raise exception.SwiftAuthError(tenant_id=context.tenant)
368
+                raise exception.SwiftAuthError(tenant_id=context.project_id)

+ 4
- 2
trove/cluster/service.py View File

@@ -143,8 +143,10 @@ class ClusterController(wsgi.Controller):
143 143
         # for all tenants.
144 144
         # * As far as I can tell this is the only call which actually uses the
145 145
         #   passed-in 'tenant_id' for anything.
146
-        if not context.is_admin and context.tenant != tenant_id:
147
-            raise exception.TroveOperationAuthError(tenant_id=context.tenant)
146
+        if not context.is_admin and context.project_id != tenant_id:
147
+            raise exception.TroveOperationAuthError(
148
+                tenant_id=context.project_id
149
+            )
148 150
 
149 151
         # The rule checks that the currently authenticated tenant can perform
150 152
         # the 'cluster-list' action.

+ 22
- 1
trove/common/apischema.py View File

@@ -123,8 +123,13 @@ volume = {
123 123
 
124 124
 nics = {
125 125
     "type": "array",
126
+    "maxItems": 1,
126 127
     "items": {
127 128
         "type": "object",
129
+        "additionalProperties": False,
130
+        "properties": {
131
+            "net-id": uuid
132
+        }
128 133
     }
129 134
 }
130 135
 
@@ -396,7 +401,23 @@ instance = {
396 401
                     "nics": nics,
397 402
                     "modules": module_list,
398 403
                     "region_name": non_empty_string,
399
-                    "locality": non_empty_string
404
+                    "locality": non_empty_string,
405
+                    "access": {
406
+                        "type": "object",
407
+                        "properties": {
408
+                            "is_public": {"type": "boolean"},
409
+                            "allowed_cidrs": {
410
+                                "type": "array",
411
+                                "uniqueItems": True,
412
+                                "items": {
413
+                                    "type": "string",
414
+                                    "pattern": "^([0-9]{1,3}\.){3}[0-9]{1,3}"
415
+                                               "(\/([0-9]|[1-2][0-9]|3[0-2]))?"
416
+                                               "$"
417
+                                }
418
+                            }
419
+                        }
420
+                    }
400 421
                 }
401 422
             }
402 423
         }

+ 42
- 13
trove/common/cfg.py View File

@@ -549,7 +549,8 @@ mysql_group = cfg.OptGroup(
549 549
     help="Oslo option group designed for MySQL datastore")
550 550
 mysql_opts = [
551 551
     cfg.BoolOpt('icmp', default=False,
552
-                help='Whether to permit ICMP.'),
552
+                help='Whether to permit ICMP.',
553
+                deprecated_for_removal=True),
553 554
     cfg.ListOpt('tcp_ports', default=["3306"], item_type=ListOfPortsType,
554 555
                 help='List of TCP ports and/or port ranges to open '
555 556
                      'in the security group (only applicable '
@@ -633,7 +634,8 @@ percona_group = cfg.OptGroup(
633 634
     help="Oslo option group designed for Percona datastore")
634 635
 percona_opts = [
635 636
     cfg.BoolOpt('icmp', default=False,
636
-                help='Whether to permit ICMP.'),
637
+                help='Whether to permit ICMP.',
638
+                deprecated_for_removal=True),
637 639
     cfg.ListOpt('tcp_ports', default=["3306"], item_type=ListOfPortsType,
638 640
                 help='List of TCP ports and/or port ranges to open '
639 641
                      'in the security group (only applicable '
@@ -721,7 +723,8 @@ pxc_group = cfg.OptGroup(
721 723
     help="Oslo option group designed for Percona XtraDB Cluster datastore")
722 724
 pxc_opts = [
723 725
     cfg.BoolOpt('icmp', default=False,
724
-                help='Whether to permit ICMP.'),
726
+                help='Whether to permit ICMP.',
727
+                deprecated_for_removal=True),
725 728
     cfg.ListOpt('tcp_ports', default=["3306", "4444", "4567", "4568"],
726 729
                 item_type=ListOfPortsType,
727 730
                 help='List of TCP ports and/or port ranges to open '
@@ -815,7 +818,8 @@ redis_group = cfg.OptGroup(
815 818
     help="Oslo option group designed for Redis datastore")
816 819
 redis_opts = [
817 820
     cfg.BoolOpt('icmp', default=False,
818
-                help='Whether to permit ICMP.'),
821
+                help='Whether to permit ICMP.',
822
+                deprecated_for_removal=True),
819 823
     cfg.ListOpt('tcp_ports', default=["6379", "16379"],
820 824
                 item_type=ListOfPortsType,
821 825
                 help='List of TCP ports and/or port ranges to open '
@@ -893,7 +897,8 @@ cassandra_group = cfg.OptGroup(
893 897
     help="Oslo option group designed for Cassandra datastore")
894 898
 cassandra_opts = [
895 899
     cfg.BoolOpt('icmp', default=False,
896
-                help='Whether to permit ICMP.'),
900
+                help='Whether to permit ICMP.',
901
+                deprecated_for_removal=True),
897 902
     cfg.ListOpt('tcp_ports', default=["7000", "7001", "7199", "9042", "9160"],
898 903
                 item_type=ListOfPortsType,
899 904
                 help='List of TCP ports and/or port ranges to open '
@@ -996,7 +1001,8 @@ couchbase_group = cfg.OptGroup(
996 1001
     help="Oslo option group designed for Couchbase datastore")
997 1002
 couchbase_opts = [
998 1003
     cfg.BoolOpt('icmp', default=False,
999
-                help='Whether to permit ICMP.'),
1004
+                help='Whether to permit ICMP.',
1005
+                deprecated_for_removal=True),
1000 1006
     cfg.ListOpt('tcp_ports', item_type=ListOfPortsType,
1001 1007
                 default=["8091", "8092", "4369", "11209-11211",
1002 1008
                          "21100-21199"],
@@ -1060,7 +1066,8 @@ mongodb_group = cfg.OptGroup(
1060 1066
     help="Oslo option group designed for MongoDB datastore")
1061 1067
 mongodb_opts = [
1062 1068
     cfg.BoolOpt('icmp', default=False,
1063
-                help='Whether to permit ICMP.'),
1069
+                help='Whether to permit ICMP.',
1070
+                deprecated_for_removal=True),
1064 1071
     cfg.ListOpt('tcp_ports', default=["2500", "27017", "27019"],
1065 1072
                 item_type=ListOfPortsType,
1066 1073
                 help='List of TCP ports and/or port ranges to open '
@@ -1158,7 +1165,8 @@ postgresql_group = cfg.OptGroup(
1158 1165
     help="Oslo option group for the PostgreSQL datastore.")
1159 1166
 postgresql_opts = [
1160 1167
     cfg.BoolOpt('icmp', default=False,
1161
-                help='Whether to permit ICMP.'),
1168
+                help='Whether to permit ICMP.',
1169
+                deprecated_for_removal=True),
1162 1170
     cfg.ListOpt('tcp_ports', default=["5432"], item_type=ListOfPortsType,
1163 1171
                 help='List of TCP ports and/or port ranges to open '
1164 1172
                      'in the security group (only applicable '
@@ -1233,7 +1241,8 @@ couchdb_group = cfg.OptGroup(
1233 1241
     help="Oslo option group designed for CouchDB datastore")
1234 1242
 couchdb_opts = [
1235 1243
     cfg.BoolOpt('icmp', default=False,
1236
-                help='Whether to permit ICMP.'),
1244
+                help='Whether to permit ICMP.',
1245
+                deprecated_for_removal=True),
1237 1246
     cfg.ListOpt('tcp_ports',
1238 1247
                 default=["5984"], item_type=ListOfPortsType,
1239 1248
                 help='List of TCP ports and/or port ranges to open '
@@ -1295,9 +1304,10 @@ vertica_group = cfg.OptGroup(
1295 1304
     help="Oslo option group designed for Vertica datastore")
1296 1305
 vertica_opts = [
1297 1306
     cfg.BoolOpt('icmp', default=False,
1298
-                help='Whether to permit ICMP.'),
1307
+                help='Whether to permit ICMP.',
1308
+                deprecated_for_removal=True),
1299 1309
     cfg.ListOpt('tcp_ports', item_type=ListOfPortsType,
1300
-                default=["5433", "5434", "22", "5444", "5450", "4803"],
1310
+                default=["5433", "5434", "5444", "5450", "4803"],
1301 1311
                 help='List of TCP ports and/or port ranges to open '
1302 1312
                      'in the security group (only applicable '
1303 1313
                      'if trove_security_groups_support is True).'),
@@ -1365,7 +1375,8 @@ db2_group = cfg.OptGroup(
1365 1375
     help="Oslo option group designed for DB2 datastore")
1366 1376
 db2_opts = [
1367 1377
     cfg.BoolOpt('icmp', default=False,
1368
-                help='Whether to permit ICMP.'),
1378
+                help='Whether to permit ICMP.',
1379
+                deprecated_for_removal=True),
1369 1380
     cfg.ListOpt('tcp_ports',
1370 1381
                 default=["50000"], item_type=ListOfPortsType,
1371 1382
                 help='List of TCP ports and/or port ranges to open '
@@ -1425,7 +1436,8 @@ mariadb_group = cfg.OptGroup(
1425 1436
     help="Oslo option group designed for MariaDB datastore")
1426 1437
 mariadb_opts = [
1427 1438
     cfg.BoolOpt('icmp', default=False,
1428
-                help='Whether to permit ICMP.'),
1439
+                help='Whether to permit ICMP.',
1440
+                deprecated_for_removal=True),
1429 1441
     cfg.ListOpt('tcp_ports', default=["3306", "4444", "4567", "4568"],
1430 1442
                 item_type=ListOfPortsType,
1431 1443
                 help='List of TCP ports and/or port ranges to open '
@@ -1545,6 +1557,21 @@ rpcapi_cap_opts = [
1545 1557
         help='Set Openstack Release compatibility for conductor services'),
1546 1558
 ]
1547 1559
 
1560
+network_group = cfg.OptGroup(
1561
+    'network',
1562
+    title='Networking options',
1563
+    help="Options related to the trove instance networking."
1564
+)
1565
+network_opts = [
1566
+    cfg.StrOpt(
1567
+        'public_network_id',
1568
+        default=None,
1569
+        help='ID of the Neutron public network to create floating IP for the '
1570
+             'public trove instance. If not given, Trove will try to query '
1571
+             'all the public networks and use the first one in the list.'
1572
+    )
1573
+]
1574
+
1548 1575
 CONF = cfg.CONF
1549 1576
 
1550 1577
 CONF.register_opts(path_opts)
@@ -1565,6 +1592,7 @@ CONF.register_group(couchdb_group)
1565 1592
 CONF.register_group(vertica_group)
1566 1593
 CONF.register_group(db2_group)
1567 1594
 CONF.register_group(mariadb_group)
1595
+CONF.register_group(network_group)
1568 1596
 
1569 1597
 CONF.register_opts(mysql_opts, mysql_group)
1570 1598
 CONF.register_opts(percona_opts, percona_group)
@@ -1578,6 +1606,7 @@ CONF.register_opts(couchdb_opts, couchdb_group)
1578 1606
 CONF.register_opts(vertica_opts, vertica_group)
1579 1607
 CONF.register_opts(db2_opts, db2_group)
1580 1608
 CONF.register_opts(mariadb_opts, mariadb_group)
1609
+CONF.register_opts(network_opts, network_group)
1581 1610
 
1582 1611
 CONF.register_opts(rpcapi_cap_opts, upgrade_levels)
1583 1612
 

+ 4
- 17
trove/common/exception.py View File

@@ -405,33 +405,16 @@ class BackupUpdateError(TroveError):
405 405
     message = _("Unable to update Backup table in database.")
406 406
 
407 407
 
408
-class SecurityGroupCreationError(TroveError):
409
-
410
-    message = _("Failed to create Security Group.")
411
-
412
-
413 408
 class SecurityGroupDeletionError(TroveError):
414 409
 
415 410
     message = _("Failed to delete Security Group.")
416 411
 
417 412
 
418
-class SecurityGroupRuleCreationError(TroveError):
419
-
420
-    message = _("Failed to create Security Group Rule.")
421
-
422
-
423 413
 class SecurityGroupRuleDeletionError(TroveError):
424 414
 
425 415
     message = _("Failed to delete Security Group Rule.")
426 416
 
427 417
 
428
-class MalformedSecurityGroupRuleError(TroveError):
429
-
430
-    message = _("Error creating security group rules."
431
-                " Malformed port(s). Port must be an integer."
432
-                " FromPort = %(from)s greater than ToPort = %(to)s.")
433
-
434
-
435 418
 class BackupNotCompleteError(TroveError):
436 419
 
437 420
     message = _("Unable to create instance because backup %(backup_id)s is "
@@ -609,6 +592,10 @@ class NetworkNotFound(TroveError):
609 592
     message = _("Network Resource %(uuid)s cannot be found.")
610 593
 
611 594
 
595
+class PublicNetworkNotFound(TroveError):
596
+    message = _("Public network cannot be found.")
597
+
598
+
612 599
 class ClusterVolumeSizeRequired(TroveError):
613 600
     message = _("A volume size is required for each instance in the cluster.")
614 601
 

+ 1
- 1
trove/common/glance_remote.py View File

@@ -37,7 +37,7 @@ def glance_client(context, region_name=None):
37 37
     if CONF.glance_url:
38 38
         endpoint_url = '%(url)s%(tenant)s' % {
39 39
             'url': normalize_url(CONF.glance_url),
40
-            'tenant': context.tenant}
40
+            'tenant': context.project_id}
41 41
     else:
42 42
         endpoint_url = get_endpoint(
43 43
             context.service_catalog, service_type=CONF.glance_service_type,

+ 1
- 1
trove/common/limits.py View File

@@ -204,7 +204,7 @@ class RateLimitingMiddleware(wsgi.TroveMiddleware):
204 204
 
205 205
         tenant_id = None
206 206
         if context:
207
-            tenant_id = context.tenant
207
+            tenant_id = context.project_id
208 208
 
209 209
         delay, error = self._limiter.check_for_delay(verb, url, tenant_id)
210 210
 

+ 100
- 0
trove/common/neutron.py View File

@@ -11,11 +11,15 @@
11 11
 #    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 12
 #    See the License for the specific language governing permissions and
13 13
 #    limitations under the License.
14
+import netaddr
15
+from oslo_log import log as logging
14 16
 
15 17
 from trove.common import cfg
18
+from trove.common import exception
16 19
 from trove.common import remote
17 20
 
18 21
 CONF = cfg.CONF
22
+LOG = logging.getLogger(__name__)
19 23
 MGMT_NETWORKS = None
20 24
 
21 25
 
@@ -47,3 +51,99 @@ def reset_management_networks():
47 51
     global MGMT_NETWORKS
48 52
 
49 53
     MGMT_NETWORKS = None
54
+
55
+
56
+def create_port(client, name, description, network_id, security_groups,
57
+                is_public=False):
58
+    port_body = {
59
+        "port": {
60
+            "name": name,
61
+            "description": description,
62
+            "network_id": network_id,
63
+            "security_groups": security_groups
64
+        }
65
+    }
66
+    port = client.create_port(body=port_body)
67
+    port_id = port['port']['id']
68
+
69
+    if is_public:
70
+        public_network_id = get_public_network(client)
71
+        if not public_network_id:
72
+            raise exception.PublicNetworkNotFound()
73
+
74
+        fip_body = {
75
+            "floatingip": {
76
+                'floating_network_id': public_network_id,
77
+                'port_id': port_id,
78
+            }
79
+        }
80
+        client.create_floatingip(fip_body)
81
+
82
+    return port_id
83
+
84
+
85
+def delete_port(client, id):
86
+    ret = client.list_floatingips(port_id=id)
87
+    if len(ret['floatingips']) > 0:
88
+        for fip in ret['floatingips']:
89
+            try:
90
+                client.delete_floatingip(fip['id'])
91
+            except Exception as e:
92
+                LOG.error(
93
+                    'Failed to delete floating IP for port %s, error: %s',
94
+                    id, str(e)
95
+                )
96
+
97
+    client.delete_port(id)
98
+
99
+
100
+def get_public_network(client):
101
+    """Get public network ID.
102
+
103
+    If not given in the config file, try to query all the public networks and
104
+    use the first one in the list.
105
+    """
106
+    if CONF.network.public_network_id:
107
+        return CONF.network.public_network_id
108
+
109
+    kwargs = {'router:external': True}
110
+    ret = client.list_networks(**kwargs)
111
+
112
+    if len(ret.get('networks', [])) == 0:
113
+        return None
114
+
115
+    return ret['networks'][0].get('id')
116
+
117
+
118
+def create_security_group(client, name, instance_id):
119
+    body = {
120
+        'security_group': {
121
+            'name': name,
122
+            'description': 'Security group for trove instance %s' % instance_id
123
+        }
124
+    }
125
+    ret = client.create_security_group(body=body)
126
+    return ret['security_group']['id']
127
+
128
+
129
+def create_security_group_rule(client, sg_id, protocol, ports, remote_ips):
130
+    for remote_ip in remote_ips:
131
+        ip = netaddr.IPNetwork(remote_ip)
132
+        ethertype = 'IPv4' if ip.version == 4 else 'IPv6'
133
+
134
+        for port_or_range in set(ports):
135
+            from_, to_ = port_or_range[0], port_or_range[-1]
136
+
137
+            body = {
138
+                "security_group_rule": {
139
+                    "direction": "ingress",
140
+                    "ethertype": ethertype,
141
+                    "protocol": protocol,
142
+                    "security_group_id": sg_id,
143
+                    "port_range_min": int(from_),
144
+                    "port_range_max": int(to_),
145
+                    "remote_ip_prefix": remote_ip
146
+                }
147
+            }
148
+
149
+            client.create_security_group_rule(body)

+ 1
- 1
trove/common/notification.py View File

@@ -364,7 +364,7 @@ class DBaaSAPINotification(object):
364 364
                                 'server_type': 'api',
365 365
                                 'client_ip': request.remote_addr,
366 366
                                 'server_ip': request.host,
367
-                                'tenant_id': context.tenant,
367
+                                'tenant_id': context.project_id,
368 368
                                 })
369 369
         elif 'request_id' not in kwargs:
370 370
             raise TroveError(_("Notification %s must include 'request'"

+ 1
- 1
trove/common/policy.py View File

@@ -69,7 +69,7 @@ def __authorize(context, rule, target=None):
69 69
        :raises:         :class:`PolicyNotAuthorized` if verification fails.
70 70
 
71 71
     """
72
-    target = target or {'tenant': context.tenant}
72
+    target = target or {'tenant': context.project_id}
73 73
     return get_enforcer().authorize(
74 74
         rule, target, context.to_dict(), do_raise=True,
75 75
         exc=trove_exceptions.PolicyNotAuthorized, action=rule)

+ 4
- 4
trove/common/remote.py View File

@@ -123,7 +123,7 @@ def cinder_client(context, region_name=None):
123 123
     if CONF.cinder_url:
124 124
         url = '%(cinder_url)s%(tenant)s' % {
125 125
             'cinder_url': normalize_url(CONF.cinder_url),
126
-            'tenant': context.tenant}
126
+            'tenant': context.project_id}
127 127
     else:
128 128
         url = get_endpoint(context.service_catalog,
129 129
                            service_type=CONF.cinder_service_type,
@@ -131,7 +131,7 @@ def cinder_client(context, region_name=None):
131 131
                            endpoint_type=CONF.cinder_endpoint_type)
132 132
 
133 133
     client = CinderClient.Client(context.user, context.auth_token,
134
-                                 project_id=context.tenant,
134
+                                 project_id=context.project_id,
135 135
                                  auth_url=CONF.trove_auth_url,
136 136
                                  insecure=CONF.cinder_api_insecure)
137 137
     client.client.auth_token = context.auth_token
@@ -143,7 +143,7 @@ def swift_client(context, region_name=None):
143 143
     if CONF.swift_url:
144 144
         # swift_url has a different format so doesn't need to be normalized
145 145
         url = '%(swift_url)s%(tenant)s' % {'swift_url': CONF.swift_url,
146
-                                           'tenant': context.tenant}
146
+                                           'tenant': context.project_id}
147 147
     else:
148 148
         url = get_endpoint(context.service_catalog,
149 149
                            service_type=CONF.swift_service_type,
@@ -152,7 +152,7 @@ def swift_client(context, region_name=None):
152 152
 
153 153
     client = Connection(preauthurl=url,
154 154
                         preauthtoken=context.auth_token,
155
-                        tenant_name=context.tenant,
155
+                        tenant_name=context.project_id,
156 156
                         snet=CONF.backup_use_snet,
157 157
                         insecure=CONF.swift_api_insecure)
158 158
     return client

+ 10
- 5
trove/common/server_group.py View File

@@ -27,16 +27,21 @@ LOG = logging.getLogger(__name__)
27 27
 class ServerGroup(object):
28 28
 
29 29
     @classmethod
30
-    def load(cls, context, compute_id):
30
+    def load(cls, context, instance_id):
31 31
         client = create_nova_client(context)
32 32
         server_group = None
33
+        expected_name = "locality_%s" % instance_id
33 34
         try:
34 35
             for sg in client.server_groups.list():
35
-                if compute_id in sg.members:
36
+                if sg.name == expected_name:
36 37
                     server_group = sg
37 38
         except Exception:
38
-            LOG.exception("Could not load server group for compute %s",
39
-                          compute_id)
39
+            LOG.exception("Could not load server group for instance %s",
40
+                          instance_id)
41
+
42
+        if not server_group:
43
+            LOG.info('No server group found for instance %s', instance_id)
44
+
40 45
         return server_group
41 46
 
42 47
     @classmethod
@@ -58,9 +63,9 @@ class ServerGroup(object):
58 63
         # it has no members
59 64
         if server_group:
60 65
             if force or len(server_group.members) <= 1:
66
+                LOG.info("Deleting server group %s", server_group.id)
61 67
                 client = create_nova_client(context)
62 68
                 client.server_groups.delete(server_group.id)
63
-                LOG.debug("Deleted server group %s.", server_group.id)
64 69
             else:
65 70
                 LOG.debug("Skipping delete of server group %(id)s "
66 71
                           "(members: %(members)s).",

+ 2
- 2
trove/common/strategies/cluster/experimental/cassandra/api.py View File

@@ -87,7 +87,7 @@ class CassandraCluster(models.Cluster):
87 87
 
88 88
         # Updating Cluster Task.
89 89
         db_info = models.DBCluster.create(
90
-            name=name, tenant_id=context.tenant,
90
+            name=name, tenant_id=context.project_id,
91 91
             datastore_version_id=datastore_version.id,
92 92
             task_status=ClusterTasks.BUILDING_INITIAL,
93 93
             configuration_id=configuration)
@@ -126,7 +126,7 @@ class CassandraCluster(models.Cluster):
126 126
         num_new_instances = len(instances)
127 127
         deltas = {'instances': num_new_instances, 'volumes': req_volume_size}
128 128
         models.assert_homogeneous_cluster(instances)
129
-        check_quotas(context.tenant, deltas)
129
+        check_quotas(context.project_id, deltas)
130 130
 
131 131
         # Checking networks are same for the cluster
132 132
         models.validate_instance_nics(context, instances)

+ 2
- 2
trove/common/strategies/cluster/experimental/galera_common/api.py View File

@@ -70,7 +70,7 @@ class GaleraCommonCluster(cluster_models.Cluster):
70 70
         deltas = {'instances': num_instances, 'volumes': req_volume_size}
71 71
 
72 72
         # quota check
73
-        check_quotas(context.tenant, deltas)
73
+        check_quotas(context.project_id, deltas)
74 74
 
75 75
         # Checking networks are same for the cluster
76 76
         cluster_models.validate_instance_nics(context, instances)
@@ -122,7 +122,7 @@ class GaleraCommonCluster(cluster_models.Cluster):
122 122
                                         datastore_version)
123 123
         # Updating Cluster Task
124 124
         db_info = cluster_models.DBCluster.create(
125
-            name=name, tenant_id=context.tenant,
125
+            name=name, tenant_id=context.project_id,
126 126
             datastore_version_id=datastore_version.id,
127 127
             task_status=ClusterTasks.BUILDING_INITIAL,
128 128
             configuration_id=configuration)

+ 4
- 4
trove/common/strategies/cluster/experimental/mongodb/api.py View File

@@ -107,7 +107,7 @@ class MongoDbCluster(models.Cluster):
107 107
             all_instances, mongo_conf.volume_support)
108 108
 
109 109
         deltas = {'instances': delta_instances, 'volumes': req_volume_size}
110
-        check_quotas(context.tenant, deltas)
110
+        check_quotas(context.project_id, deltas)
111 111
 
112 112
         # Checking networks are same for the cluster
113 113
         models.validate_instance_nics(context, instances)
@@ -121,7 +121,7 @@ class MongoDbCluster(models.Cluster):
121 121
                    for instance in instances]
122 122
 
123 123
         db_info = models.DBCluster.create(
124
-            name=name, tenant_id=context.tenant,
124
+            name=name, tenant_id=context.project_id,
125 125
             datastore_version_id=datastore_version.id,
126 126
             task_status=ClusterTasks.BUILDING_INITIAL)
127 127
 
@@ -297,7 +297,7 @@ class MongoDbCluster(models.Cluster):
297 297
         volume_size = a_member.volume_size
298 298
         if volume_size:
299 299
             deltas['volumes'] = volume_size * num_members_per_shard
300
-        check_quotas(self.context.tenant, deltas)
300
+        check_quotas(self.context.project_id, deltas)
301 301
         new_replica_set_name = "rs" + str(num_unique_shards + 1)
302 302
         new_shard_id = utils.generate_uuid()
303 303
         dsv_manager = (datastore_models.DatastoreVersion.
@@ -622,7 +622,7 @@ class MongoDbCluster(models.Cluster):
622 622
         deltas = {'instances': len(instances),
623 623
                   'volumes': sum([instance['volume_size']
624 624
                                   for instance in instances])}
625
-        check_quotas(context.tenant, deltas)
625
+        check_quotas(context.project_id, deltas)
626 626
 
627 627
     @staticmethod
628 628
     def _check_instances(context, instances, datastore_version,

+ 2
- 2
trove/common/strategies/cluster/experimental/redis/api.py View File

@@ -74,7 +74,7 @@ class RedisCluster(models.Cluster):
74 74
         # Check quotas
75 75
         quota_request = {'instances': num_instances,
76 76
                          'volumes': total_volume_allocation}
77
-        check_quotas(context.tenant, quota_request)
77
+        check_quotas(context.project_id, quota_request)
78 78
 
79 79
         # Creating member instances
80 80
         return [inst_models.Instance.create(context,
@@ -112,7 +112,7 @@ class RedisCluster(models.Cluster):
112 112
         # Updating Cluster Task
113 113
 
114 114
         db_info = models.DBCluster.create(
115
-            name=name, tenant_id=context.tenant,
115
+            name=name, tenant_id=context.project_id,
116 116
             datastore_version_id=datastore_version.id,
117 117
             task_status=ClusterTasks.BUILDING_INITIAL)
118 118
 

+ 2
- 2
trove/common/strategies/cluster/experimental/vertica/api.py View File

@@ -96,7 +96,7 @@ class VerticaCluster(models.Cluster):
96 96
 
97 97
         deltas = {'instances': num_instances, 'volumes': req_volume_size}
98 98
 
99
-        check_quotas(context.tenant, deltas)
99
+        check_quotas(context.project_id, deltas)
100 100
 
101 101
         flavor_id = instances[0]['flavor_id']
102 102
         volume_size = instances[0].get('volume_size', None)
@@ -149,7 +149,7 @@ class VerticaCluster(models.Cluster):
149 149
                 num_instances=vertica_conf.cluster_member_count)
150 150
 
151 151
         db_info = models.DBCluster.create(
152
-            name=name, tenant_id=context.tenant,
152
+            name=name, tenant_id=context.project_id,
153 153
             datastore_version_id=datastore_version.id,
154 154
             task_status=ClusterTasks.BUILDING_INITIAL)
155 155
 

+ 2
- 2
trove/common/trove_remote.py View File

@@ -36,7 +36,7 @@ def trove_client(context, region_name=None):
36 36
     if CONF.trove_url:
37 37
         url = '%(url)s%(tenant)s' % {
38 38
             'url': normalize_url(CONF.trove_url),
39
-            'tenant': context.tenant}
39
+            'tenant': context.project_id}
40 40
     else:
41 41
         url = get_endpoint(context.service_catalog,
42 42
                            service_type=CONF.trove_service_type,
@@ -44,7 +44,7 @@ def trove_client(context, region_name=None):
44 44
                            endpoint_type=CONF.trove_endpoint_type)
45 45
 
46 46
     client = TroveClient.Client(context.user, context.auth_token,
47
-                                project_id=context.tenant,
47
+                                project_id=context.project_id,
48 48
                                 auth_url=CONF.trove_auth_url)
49 49
     client.client.auth_token = context.auth_token
50 50
     client.client.management_url = url

+ 7
- 4
trove/common/utils.py View File

@@ -185,16 +185,19 @@ class MethodInspector(object):
185 185
 
186 186
 def build_polling_task(retriever, condition=lambda value: value,
187 187
                        sleep_time=1, time_out=0):
188
+    """Run a function in a loop with backoff on error.
189
+
190
+    The condition function runs based on the retriever function result.
191
+    """
188 192
 
189 193
     def poll_and_check():
190 194
         obj = retriever()
191 195
         if condition(obj):
192 196
             raise loopingcall.LoopingCallDone(retvalue=obj)
193 197
 
194
-    return loopingcall.BackOffLoopingCall(
195
-        f=poll_and_check).start(initial_delay=False,
196
-                                starting_interval=sleep_time,
197
-                                max_interval=30, timeout=time_out)
198
+    call = loopingcall.BackOffLoopingCall(f=poll_and_check)
199
+    return call.start(initial_delay=False, starting_interval=sleep_time,
200
+                      max_interval=30, timeout=time_out)
198 201
 
199 202
 
200 203
 def wait_for_task(polling_task):

+ 1
- 1
trove/common/views.py View File

@@ -23,7 +23,7 @@ def create_links(resource_path, request, id):
23 23
     link_info = {
24 24
         'host': request.host,
25 25
         'version': request.url_version,
26
-        'tenant_id': context.tenant,
26
+        'tenant_id': context.project_id,
27 27
         'resource_path': resource_path,
28 28
         'id': id,
29 29
     }

+ 3
- 3
trove/configuration/models.py View File

@@ -48,11 +48,11 @@ class Configurations(object):
48 48
             if db_info.count() == 0:
49 49
                 LOG.debug("No configurations found for admin user")
50 50
         else:
51
-            db_info = DBConfiguration.find_all(tenant_id=context.tenant,
51
+            db_info = DBConfiguration.find_all(tenant_id=context.project_id,
52 52
                                                deleted=False)
53 53
             if db_info.count() == 0:
54 54
                 LOG.debug("No configurations found for tenant %s",
55
-                          context.tenant)
55
+                          context.project_id)
56 56
 
57 57
         limit = utils.pagination_limit(context.limit,
58 58
                                        Configurations.DEFAULT_LIMIT)
@@ -133,7 +133,7 @@ class Configuration(object):
133 133
                 return DBConfiguration.find_by(id=id, deleted=False)
134 134
             else:
135 135
                 return DBConfiguration.find_by(id=id,
136
-                                               tenant_id=context.tenant,
136
+                                               tenant_id=context.project_id,
137 137
                                                deleted=False)
138 138
         except ModelNotFoundError:
139 139
             msg = _("Configuration group with ID %s could not be found.") % id

+ 3
- 3
trove/configuration/service.py View File

@@ -66,7 +66,7 @@ class ConfigurationsController(wsgi.Controller):
66 66
         configuration_items = models.Configuration.load_items(context, id)
67 67
 
68 68
         configuration.instance_count = instances_models.DBInstance.find_all(
69
-            tenant_id=context.tenant,
69
+            tenant_id=context.project_id,
70 70
             configuration_id=configuration.id,
71 71
             deleted=False).count()
72 72
 
@@ -154,7 +154,7 @@ class ConfigurationsController(wsgi.Controller):
154 154
             context, request=req)
155 155
         with StartNotification(context, configuration_id=id):
156 156
             instances = instances_models.DBInstance.find_all(
157
-                tenant_id=context.tenant,
157
+                tenant_id=context.project_id,
158 158
                 configuration_id=id,
159 159
                 deleted=False).all()
160 160
             if instances:
@@ -221,7 +221,7 @@ class ConfigurationsController(wsgi.Controller):
221 221
         LOG.debug("Re-applying configuration group '%s' to all instances.",
222 222
                   configuration_id)
223 223
         single_instances = instances_models.DBInstance.find_all(
224
-            tenant_id=context.tenant,
224
+            tenant_id=context.project_id,
225 225
             configuration_id=configuration_id,
226 226
             cluster_id=None,
227 227
             deleted=False).all()

+ 2
- 2
trove/db/models.py View File

@@ -104,13 +104,13 @@ class DatabaseModelBase(models.ModelBase):
104 104
                                                {"s_name": cls.__name__})
105 105
 
106 106
         if ((context and not context.is_admin and hasattr(model, 'tenant_id')
107
-             and model.tenant_id != context.tenant)):
107
+             and model.tenant_id != context.project_id)):
108 108
             log_fmt = ("Tenant %(s_tenant)s tried to access "
109 109
                        "%(s_name)s, owned by %(s_owner)s.")
110 110
             exc_fmt = _("Tenant %(s_tenant)s tried to access "
111 111
                         "%(s_name)s, owned by %(s_owner)s.")
112 112
             msg_content = {
113
-                "s_tenant": context.tenant,
113
+                "s_tenant": context.project_id,
114 114
                 "s_name": cls.__name__,
115 115
                 "s_owner": model.tenant_id}
116 116
             LOG.error(log_fmt, msg_content)

+ 0
- 0
trove/extensions/account/__init__.py View File


+ 0
- 58
trove/extensions/account/models.py View File

@@ -1,58 +0,0 @@
1
-# Copyright 2010-2011 OpenStack Foundation
2
-# All Rights Reserved.
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
-from oslo_log import log as logging
17
-
18
-from trove.instance.models import DBInstance
19
-
20
-
21
-LOG = logging.getLogger(__name__)
22
-
23
-
24
-class Account(object):
25
-    """Shows all trove instance ids owned by an account."""
26
-
27
-    def __init__(self, id, instance_ids):
28
-        self.id = id
29
-        self.instance_ids = instance_ids
30
-
31
-    @staticmethod
32
-    def load(context, id):
33
-        db_infos = DBInstance.find_all(tenant_id=id, deleted=False)
34
-        instance_ids = []
35
-        for db_info in db_infos:
36
-            instance_ids.append(db_info.id)
37
-        return Account(id, instance_ids)
38
-
39
-
40
-class AccountsSummary(object):
41
-
42
-    def __init__(self, accounts):
43
-        self.accounts = accounts
44
-
45
-    @classmethod
46
-    def load(cls):
47
-        # TODO(pdmars): This should probably be changed to a more generic
48
-        # database filter query if one is added, however, this should suffice
49
-        # for now.
50
-        db_infos = DBInstance.find_all(deleted=False)
51
-        tenant_ids_for_instances = [db_info.tenant_id for db_info in db_infos]
52
-        tenant_ids = set(tenant_ids_for_instances)
53
-        LOG.debug("All tenants with instances: %s", tenant_ids)
54
-        accounts = []
55
-        for tenant_id in tenant_ids:
56
-            num_instances = tenant_ids_for_instances.count(tenant_id)
57
-            accounts.append({'id': tenant_id, 'num_instances': num_instances})
58
-        return cls(accounts)

+ 0
- 48
trove/extensions/account/service.py View File

@@ -1,48 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation
2
-# All Rights Reserved.
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
-from oslo_log import log as logging
17
-
18
-import trove.common.apischema as apischema
19
-from trove.common.auth import admin_context
20
-from trove.common import wsgi
21
-from trove.extensions.account import models
22
-from trove.extensions.account import views
23
-
24
-LOG = logging.getLogger(__name__)
25
-
26
-
27
-class AccountController(wsgi.Controller):
28
-    """Controller for account functionality."""
29
-    schemas = apischema.account
30
-
31
-    @admin_context
32
-    def show(self, req, tenant_id, id):
33
-        """Return a account and instances associated with a single account."""
34
-        LOG.info("req : '%s'\n\n", req)
35
-        LOG.info("Showing account information for '%(account)s' "
36
-                 "to '%(tenant)s'", {'account': id, 'tenant': tenant_id})
37
-
38
-        context = req.environ[wsgi.CONTEXT_KEY]
39
-        account = models.Account.load(context, id)
40
-        return wsgi.Result(views.AccountView(account).data(), 200)
41
-
42
-    @admin_context
43
-    def index(self, req, tenant_id):
44
-        """Return a list of all accounts with non-deleted instances."""
45
-        LOG.info("req : '%s'\n\n", req)
46
-        LOG.info("Showing all accounts with instances for '%s'", tenant_id)
47
-        accounts_summary = models.AccountsSummary.load()
48
-        return wsgi.Result(views.AccountsView(accounts_summary).data(), 200)

+ 0
- 37
trove/extensions/account/views.py View File

@@ -1,37 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation
2
-# All Rights Reserved.
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
-
17
-class AccountsView(object):
18
-
19
-    def __init__(self, accounts_summary):
20
-        self.accounts_summary = accounts_summary
21
-
22
-    def data(self):
23
-        return {'accounts': self.accounts_summary.accounts}
24
-
25
-
26
-class AccountView(object):
27
-
28
-    def __init__(self, account):
29
-        self.account = account
30
-
31
-    def data(self):
32
-        return {
33
-            'account': {
34
-                'id': self.account.id,
35
-                'instance_ids': self.account.instance_ids,
36
-            }
37
-        }

+ 0
- 0
trove/extensions/mgmt/host/__init__.py View File


+ 0
- 0
trove/extensions/mgmt/host/instance/__init__.py View File


+ 0
- 60
trove/extensions/mgmt/host/instance/service.py View File

@@ -1,60 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation
2
-# All Rights Reserved.
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
-
17
-from oslo_log import log as logging
18
-
19
-from trove.common import exception
20
-from trove.common.i18n import _
21
-from trove.common import wsgi
22
-from trove.extensions.mgmt.host import models
23
-
24
-LOG = logging.getLogger(__name__)
25
-
26
-
27
-class HostInstanceController(wsgi.Controller):
28
-    """Controller for all instances on specific hosts."""
29
-
30
-    def action(self, req, body, tenant_id, host_id):
31
-        LOG.info("Committing an ACTION against host %(host_id)s for "
32
-                 "tenant '%(tenant_id)s'\n"
33
-                 "req : '%(req)s'\n\n", {"req": req, "host_id": host_id,
34
-                                         "tenant_id": tenant_id})
35
-
36
-        if not body:
37
-            raise exception.BadRequest(_("Invalid request body."))
38
-        context = req.environ[wsgi.CONTEXT_KEY]
39
-        host = models.DetailedHost.load(context, host_id)
40
-        _actions = {'update': self._action_update}
41
-        selected_action = None
42
-        for key in body:
43
-            if key in _actions:
44
-                if selected_action is not None:
45
-                    msg = _("Only one action can be specified per request.")
46
-                    raise exception.BadRequest(msg)
47
-                selected_action = _actions[key]
48
-            else:
49
-                msg = _("Invalid host action: %s") % key
50
-                raise exception.BadRequest(msg)
51
-
52
-        if selected_action:
53
-            return selected_action(context, host, body)
54
-        else:
55
-            raise exception.BadRequest(_("Invalid request body."))
56
-
57
-    def _action_update(self, context, host, body):
58
-        LOG.debug("Updating all instances for host: %s", host.name)
59
-        host.update_all(context)
60
-        return wsgi.Result(None, 202)

+ 0
- 102
trove/extensions/mgmt/host/models.py View File

@@ -1,102 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation
2
-# All Rights Reserved.
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
-"""
17
-Model classes that extend the instances functionality for MySQL instances.
18
-"""
19
-
20
-from novaclient import exceptions as nova_exceptions
21
-from oslo_log import log as logging
22
-
23
-from trove.common import exception
24
-from trove.common.i18n import _
25
-from trove.common.remote import create_guest_client
26
-from trove.common.remote import create_nova_client
27
-from trove.instance.models import DBInstance
28
-from trove.instance.models import InstanceServiceStatus
29
-from trove.instance.models import SimpleInstance
30
-
31
-
32
-LOG = logging.getLogger(__name__)
33
-
34
-
35
-class SimpleHost(object):
36
-
37
-    def __init__(self, name, instance_count):
38
-        self.name = name
39
-        self.instance_count = instance_count
40
-
41
-    @staticmethod
42
-    def load_all(context):
43
-        client = create_nova_client(context)
44
-        LOG.debug("Client.rdhosts=" + str(client.rdhosts))
45
-        rdhosts = client.rdhosts.list()
46
-        LOG.debug("RDHOSTS=" + str(rdhosts))
47
-        for rdhost in rdhosts:
48
-            LOG.debug("rdhost=" + str(rdhost))
49
-        return [SimpleHost(rdhost.name, rdhost.instanceCount)
50
-                for rdhost in rdhosts]
51
-
52
-
53
-class DetailedHost(object):
54
-
55
-    def __init__(self, host_info):
56
-        self.name = host_info.name
57
-        self.percent_used = host_info.percentUsed
58
-        self.total_ram = host_info.totalRAM
59
-        self.used_ram = host_info.usedRAM
60
-        self.instances = host_info.instances
61
-        for instance in self.instances:
62
-            instance['server_id'] = instance['uuid']
63
-            del instance['uuid']
64
-            try:
65
-                db_info = DBInstance.find_by(
66
-                    compute_instance_id=instance['server_id'])
67
-                instance['id'] = db_info.id
68
-                instance['tenant_id'] = db_info.tenant_id
69
-                status = InstanceServiceStatus.find_by(
70
-                    instance_id=db_info.id)
71
-                instance_info = SimpleInstance(None, db_info, status)
72
-                instance['status'] = instance_info.status
73
-            except exception.TroveError as re:
74
-                LOG.error(re)
75
-                LOG.error("Compute Instance ID found with no associated RD "
76
-                          "instance: %s.", instance['server_id'])
77
-                instance['id'] = None
78
-
79
-    def update_all(self, context):
80
-        num_i = len(self.instances)
81
-        LOG.debug("Host %(name)s has %(num)s instances to update.",
82
-                  {'name': self.name, 'num': num_i})
83
-        failed_instances = []
84
-        for instance in self.instances:
85
-            client = create_guest_client(context, instance['id'])
86
-            try:
87
-                client.update_guest()
88
-            except exception.TroveError as re:
89
-                LOG.error(re)
90
-                LOG.error("Unable to update instance: %s.", instance['id'])
91
-                failed_instances.append(instance['id'])
92
-        if len(failed_instances) > 0:
93
-            msg = _("Failed to update instances: %s.") % failed_instances
94
-            raise exception.UpdateGuestError(msg)
95
-
96
-    @staticmethod
97
-    def load(context, name):
98
-        client = create_nova_client(context)
99
-        try:
100
-            return DetailedHost(client.rdhosts.get(name))
101
-        except nova_exceptions.NotFound:
102
-            raise exception.NotFound(uuid=name)

+ 0
- 47
trove/extensions/mgmt/host/service.py View File

@@ -1,47 +0,0 @@
1
-# Copyright 2011 OpenStack Foundation
2
-# All Rights Reserved.
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
-from oslo_log import log as logging
17
-
18
-from trove.common.auth import admin_context
19
-from trove.common import wsgi
20
-from trove.extensions.mgmt.host import models
21
-from trove.extensions.mgmt.host import views
22
-from trove.instance.service import InstanceController
23
-
24
-LOG = logging.getLogger(__name__)
25
-
26
-
27
-class HostController(InstanceController):
28
-    """Controller for instance functionality."""
29
-
30
-    @admin_context
31
-    def index(self, req, tenant_id, detailed=False):
32
-        """Return all hosts."""
33
-        LOG.info("req : '%s'\n\n", req)
34
-        LOG.info("Indexing a host for tenant '%s'", tenant_id)
35
-        context = req.environ[wsgi.CONTEXT_KEY]
36
-        hosts = models.SimpleHost.load_all(context)
37
-        return wsgi.Result(views.HostsView(hosts).data(), 200)
38
-
39
-    @admin_context
40
-    def show(self, req, tenant_id, id):
41
-        """Return a single host."""
42
-        LOG.info("req : '%s'\n\n", req)
43
-        LOG.info("Showing a host for tenant '%s'", tenant_id)
44
-        LOG.info("id : '%s'\n\n", id)
45
-        context = req.environ[wsgi.CONTEXT_KEY]
46
-        host = models.DetailedHost.load(context, id)
47
-        return wsgi.Result(views.HostDetailedView(host).data(), 200)

+ 0
- 51
trove/extensions/mgmt/host/views.py View File

@@ -1,51 +0,0 @@
1
-# Copyright 2011 OpenStack Foundation
2
-# All Rights Reserved.
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
-
17
-class HostView(object):
18
-
19
-    def __init__(self, host):
20
-        self.host = host
21
-
22
-    def data(self):
23
-        return {
24
-            'instanceCount': self.host.instance_count,
25
-            'name': self.host.name
26
-        }
27
-
28
-
29
-class HostDetailedView(object):
30
-
31
-    def __init__(self, host):
32
-        self.host = host
33
-
34
-    def data(self):
35
-        return {'host': {
36
-            'instances': self.host.instances,
37
-            'name': self.host.name,
38
-            'percentUsed': self.host.percent_used,
39
-            'totalRAM': self.host.total_ram,
40
-            'usedRAM': self.host.used_ram
41
-        }}
42
-
43
-
44
-class HostsView(object):
45
-
46
-    def __init__(self, hosts):
47
-        self.hosts = hosts
48
-
49
-    def data(self):
50
-        data = [HostView(host).data() for host in self.hosts]
51
-        return {'hosts': data}

+ 0
- 0
trove/extensions/mgmt/volume/__init__.py View File


+ 0
- 50
trove/extensions/mgmt/volume/models.py View File

@@ -1,50 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation
2
-# All Rights Reserved.
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
-"""
17
-Model classes that extend the instances functionality for volumes.
18
-"""
19
-
20
-from oslo_log import log as logging
21
-
22
-from trove.common.remote import create_cinder_client
23
-
24
-
25
-LOG = logging.getLogger(__name__)
26
-
27
-
28
-class StorageDevice(object):
29
-
30
-    def __init__(self, storage_info):
31
-        self.name = storage_info.name
32
-        self.type = storage_info.type
33
-        self.total_space = storage_info.capacity['total']
34
-        self.total_avail = storage_info.capacity['available']
35
-        self.prov_total = storage_info.provision['total']
36
-        self.prov_avail = storage_info.provision['available']
37
-        self.prov_percent = storage_info.provision['percent']
38
-        self.used = storage_info.used
39
-
40
-
41
-class StorageDevices(object):
42
-
43
-    @staticmethod
44
-    def load(context, region_name):
45
-        client = create_cinder_client(context, region_name)
46
-        rdstorages = client.rdstorage.list()
47
-        for rdstorage in rdstorages:
48
-            LOG.debug("rdstorage=" + str(rdstorage))
49
-        return [StorageDevice(storage_info)
50
-                for storage_info in rdstorages]

+ 0
- 39
trove/extensions/mgmt/volume/service.py View File

@@ -1,39 +0,0 @@
1
-# Copyright 2011 OpenStack Foundation
2
-# All Rights Reserved.
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
-
17
-from oslo_log import log as logging
18
-
19
-from trove.common.auth import admin_context
20
-from trove.common import cfg
21
-from trove.common import wsgi
22
-from trove.extensions.mgmt.volume import models
23
-from trove.extensions.mgmt.volume import views
24
-
25
-CONF = cfg.CONF
26
-LOG = logging.getLogger(__name__)
27
-
28
-
29
-class StorageController(wsgi.Controller):
30
-    """Controller for storage device functionality."""
31
-
32
-    @admin_context
33
-    def index(self, req, tenant_id):
34
-        """Return all storage devices."""
35
-        LOG.info("req : '%s'\n\n", req)
36
-        LOG.info("Indexing storage info for tenant '%s'", tenant_id)
37
-        context = req.environ[wsgi.CONTEXT_KEY]
38
-        storages = models.StorageDevices.load(context, CONF.os_region_name)
39
-        return wsgi.Result(views.StoragesView(storages).data(), 200)

+ 0
- 40
trove/extensions/mgmt/volume/views.py View File

@@ -1,40 +0,0 @@
1
-# Copyright 2011 OpenStack Foundation
2
-# All Rights Reserved.
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
-
17
-class StorageView(object):
18
-
19
-    def __init__(self, storage):
20
-        self.storage = storage
21
-
22
-    def data(self):
23
-        return {'name': self.storage.name,
24
-                'type': self.storage.type,
25
-                'capacity': {'total': self.storage.total_space,
26
-                             'available': self.storage.total_avail},
27
-                'provision': {'total': self.storage.prov_total,
28
-                              'available': self.storage.prov_avail,
29
-                              'percent': self.storage.prov_percent},
30
-                'used': self.storage.used}
31
-
32
-
33
-class StoragesView(object):
34
-
35
-    def __init__(self, storages):
36
-        self.storages = storages
37
-
38
-    def data(self):
39
-        data = [StorageView(storage).data() for storage in self.storages]
40
-        return {'devices': data}

+ 0
- 44
trove/extensions/routes/account.py View File

@@ -1,44 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation
2
-# All Rights Reserved.
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
-from trove.common import extensions
17
-from trove.extensions.account import service
18
-
19
-
20
-class Account(extensions.ExtensionDescriptor):
21
-
22
-    def get_name(self):
23
-        return "Account"
24
-
25
-    def get_description(self):
26
-        return "Account information with instances"
27
-
28
-    def get_alias(self):
29
-        return "Account"
30
-
31
-    def get_namespace(self):
32
-        return "http://TBD"
33
-
34
-    def get_updated(self):
35
-        return "2012-06-07T13:25:27-06:00"
36
-
37
-    def get_resources(self):
38
-        resources = []
39
-        resource = extensions.ResourceExtension(
40
-            '{tenant_id}/mgmt/accounts',
41
-            service.AccountController())
42
-        resources.append(resource)
43
-
44
-        return resources

+ 0
- 23
trove/extensions/routes/mgmt.py View File

@@ -17,12 +17,9 @@ from trove.common import extensions
17 17
 from trove.extensions.mgmt.clusters.service import MgmtClusterController
18 18
 from trove.extensions.mgmt.configuration import service as conf_service
19 19
 from trove.extensions.mgmt.datastores.service import DatastoreVersionController
20
-from trove.extensions.mgmt.host.instance import service as hostservice
21
-from trove.extensions.mgmt.host.service import HostController
22 20
 from trove.extensions.mgmt.instances.service import MgmtInstanceController
23 21
 from trove.extensions.mgmt.quota.service import QuotaController
24 22
 from trove.extensions.mgmt.upgrade.service import UpgradeController
25
-from trove.extensions.mgmt.volume.service import StorageController
26 23
 
27 24
 
28 25
 class Mgmt(extensions.ExtensionDescriptor):
@@ -61,32 +58,12 @@ class Mgmt(extensions.ExtensionDescriptor):
61 58
             member_actions={'action': 'POST'})
62 59
         resources.append(clusters)
63 60
 
64
-        hosts = extensions.ResourceExtension(
65
-            '{tenant_id}/mgmt/hosts',
66
-            HostController(),
67
-            member_actions={})
68
-        resources.append(hosts)
69
-
70 61
         quota = extensions.ResourceExtension(
71 62
             '{tenant_id}/mgmt/quotas',
72 63
             QuotaController(),
73 64
             member_actions={})
74 65
         resources.append(quota)
75 66
 
76
-        storage = extensions.ResourceExtension(
77
-            '{tenant_id}/mgmt/storage',
78
-            StorageController(),
79
-            member_actions={})
80
-        resources.append(storage)
81
-
82
-        host_instances = extensions.ResourceExtension(
83
-            'instances',
84
-            hostservice.HostInstanceController(),
85
-            parent={'member_name': 'host',
86
-                    'collection_name': '{tenant_id}/mgmt/hosts'},
87
-            collection_actions={'action': 'POST'})
88
-        resources.append(host_instances)
89
-
90 67
         upgrade = extensions.ResourceExtension(
91 68
             '{tenant_id}/mgmt/instances/{instance_id}/upgrade',
92 69
             UpgradeController(),

+ 4
- 94
trove/extensions/security_group/models.py View File

@@ -46,47 +46,8 @@ class SecurityGroup(DatabaseModelBase):
46 46
 
47 47
     @property
48 48
     def instance_id(self):
49
-        return SecurityGroupInstanceAssociation\
50
-            .get_instance_id_by_security_group_id(self.id)
51
-
52
-    @classmethod
53
-    def create_sec_group(cls, name, description, context, region_name):
54
-        try:
55
-            remote_sec_group = RemoteSecurityGroup.create(
56
-                name, description, context, region_name)
57
-
58
-            if not remote_sec_group:
59
-                raise exception.SecurityGroupCreationError(
60
-                    _("Failed to create Security Group."))
61
-            else:
62
-                return cls.create(
63
-                    id=remote_sec_group.data()['id'],
64
-                    name=name,
65
-                    description=description,
66
-                    user=context.user,
67
-                    tenant_id=context.tenant)
68
-
69
-        except exception.SecurityGroupCreationError:
70
-            LOG.exception("Failed to create remote security group.")
71
-            raise
72
-
73
-    @classmethod
74
-    def create_for_instance(cls, instance_id, context, region_name):
75
-        # Create a new security group
76
-        name = "%s_%s" % (CONF.trove_security_group_name_prefix, instance_id)
77
-        description = _("Security Group for %s") % instance_id
78
-        sec_group = cls.create_sec_group(name, description, context,
79
-                                         region_name)
80
-
81
-        # Currently this locked down by default, since we don't create any
82
-        # default security group rules for the security group.
83
-
84
-        # Create security group instance association
85
-        SecurityGroupInstanceAssociation.create(
86
-            security_group_id=sec_group["id"],
87
-            instance_id=instance_id)
88
-
89
-        return sec_group
49
+        return SecurityGroupInstanceAssociation.\
50
+            get_instance_id_by_security_group_id(self.id)
90 51
 
91 52
     @classmethod
92 53
     def get_security_group_by_id_or_instance_id(cls, id, tenant_id):
@@ -127,11 +88,8 @@ class SecurityGroup(DatabaseModelBase):
127 88
                 if sec_group:
128 89
                     sec_group.delete(context, region_name)
129 90
                 association.delete()
130
-        except (exception.ModelNotFoundError,
131
-                exception.TroveError):
132
-            LOG.info('Security Group with id: %(id)s '
133
-                     'already had been deleted',
134
-                     {'id': instance_id})
91
+        except (exception.ModelNotFoundError, exception.TroveError):
92
+            pass
135 93
 
136 94
 
137 95
 class SecurityGroupRule(DatabaseModelBase):
@@ -140,36 +98,6 @@ class SecurityGroupRule(DatabaseModelBase):
140 98
                     'updated', 'deleted', 'deleted_at']
141 99
     _table_name = 'security_group_rules'
142 100
 
143
-    @classmethod
144
-    def create_sec_group_rule(cls, sec_group, protocol, from_port,
145
-                              to_port, cidr, context, region_name):
146
-        try:
147
-            remote_rule_id = RemoteSecurityGroup.add_rule(
148
-                sec_group_id=sec_group['id'],
149
-                protocol=protocol,
150
-                from_port=from_port,
151
-                to_port=to_port,
152
-                cidr=cidr,
153
-                context=context,
154
-                region_name=region_name)
155
-
156
-            if not remote_rule_id:
157
-                raise exception.SecurityGroupRuleCreationError(
158
-                    "Failed to create Remote Security Group Rule")
159
-            else:
160
-                # Create db record
161
-                return cls.create(
162
-                    id=remote_rule_id,
163
-                    protocol=protocol,
164
-                    from_port=from_port,
165
-                    to_port=to_port,
166
-                    cidr=cidr,
167
-                    group_id=sec_group['id'])
168
-
169
-        except exception.SecurityGroupRuleCreationError:
170
-            LOG.exception("Failed to create remote security group rule.")
171
-            raise
172
-
173 101
     def get_security_group(self, tenant_id):
174 102
         return SecurityGroup.find_by(id=self.group_id,
175 103
                                      tenant_id=tenant_id,
@@ -226,30 +154,12 @@ class RemoteSecurityGroup(NetworkRemoteModelBase):
226 154
         else:
227 155
             self._data_object = security_group
228 156
 
229
-    @classmethod
230
-    def create(cls, name, description, context, region_name):
231
-        """Creates a new Security Group."""
232
-        driver = cls.get_driver(context, region_name)
233
-        sec_group = driver.create_security_group(
234
-            name=name, description=description)
235
-        return RemoteSecurityGroup(security_group=sec_group)
236
-
237 157
     @classmethod
238 158
     def delete(cls, sec_group_id, context, region_name):
239 159
         """Deletes a Security Group."""
240 160
         driver = cls.get_driver(context, region_name)
241 161
         driver.delete_security_group(sec_group_id)
242 162
 
243
-    @classmethod
244
-    def add_rule(cls, sec_group_id, protocol, from_port,
245
-                 to_port, cidr, context, region_name):
246
-        """Adds a new rule to an existing security group."""
247
-        driver = cls.get_driver(context, region_name)
248
-        sec_group_rule = driver.add_security_group_rule(
249
-            sec_group_id, protocol, from_port, to_port, cidr)
250
-
251
-        return sec_group_rule.id
252
-
253 163
     @classmethod
254 164
     def delete_rule(cls, sec_group_rule_id, context, region_name):
255 165
         """Deletes a rule from an existing security group."""

+ 1
- 1
trove/guestagent/pkg.py View File

@@ -292,7 +292,7 @@ class DebianPackagerMixin(BasePackagerMixin):
292 292
         if selections:
293 293
             with NamedTemporaryFile(delete=False) as f:
294 294
                 fname = f.name
295
-                f.write(selections)
295
+                f.write(encodeutils.safe_encode(selections))
296 296
             try:
297 297
                 utils.execute("debconf-set-selections", fname,
298 298
                               run_as_root=True, root_helper="sudo")

+ 17
- 13
trove/instance/models.py View File

@@ -565,7 +565,7 @@ def load_instance_with_info(cls, context, id, cluster_id=None):
565 565
               {'instance_id': id, 'service_status': service_status.status})
566 566
     instance = cls(context, db_info, service_status)
567 567
     load_guest_info(instance, context, id)
568
-    load_server_group_info(instance, context, db_info.compute_instance_id)
568
+    load_server_group_info(instance, context)
569 569
     return instance
570 570
 
571 571
 
@@ -581,8 +581,9 @@ def load_guest_info(instance, context, id):
581 581
     return instance
582 582
 
583 583
 
584
-def load_server_group_info(instance, context, compute_id):
585
-    server_group = srv_grp.ServerGroup.load(context, compute_id)
584
+def load_server_group_info(instance, context):
585
+    instance_id = instance.slave_of_id if instance.slave_of_id else instance.id
586
+    server_group = srv_grp.ServerGroup.load(context, instance_id)
586 587
     if server_group:
587 588
         instance.locality = srv_grp.ServerGroup.get_locality(server_group)
588 589
 
@@ -675,8 +676,9 @@ class BaseInstance(SimpleInstance):
675 676
                        task_status=InstanceTasks.NONE)
676 677
         self.set_servicestatus_deleted()
677 678
         self.set_instance_fault_deleted()
678
-        # Delete associated security group
679
+
679 680
         if CONF.trove_security_groups_support:
681
+            # Delete associated security group for backward compatibility
680 682
             SecurityGroup.delete_for_instance(self.db_info.id, self.context,
681 683
                                               self.db_info.region_id)
682 684
 
@@ -736,8 +738,8 @@ class BaseInstance(SimpleInstance):
736 738
     def server_group(self):
737 739
         # The server group could be empty, so we need a flag to cache it
738 740
         if not self._server_group_loaded:
739
-            self._server_group = srv_grp.ServerGroup.load(
740
-                self.context, self.db_info.compute_instance_id)
741
+            self._server_group = srv_grp.ServerGroup.load(self.context,
742
+                                                          self.id)
741 743
             self._server_group_loaded = True
742 744
         return self._server_group
743 745
 
@@ -868,7 +870,7 @@ class Instance(BuiltInstance):
868 870
                availability_zone=None, nics=None,
869 871
                configuration_id=None, slave_of_id=None, cluster_config=None,
870 872
                replica_count=None, volume_type=None, modules=None,
871
-               locality=None, region_name=None):
873
+               locality=None, region_name=None, access=None):
872 874
 
873 875
         region_name = region_name or CONF.os_region_name
874 876
 
@@ -1052,7 +1054,8 @@ class Instance(BuiltInstance):
1052 1054
             root_password = None
1053 1055
             for instance_index in range(0, instance_count):
1054 1056
                 db_info = DBInstance.create(
1055
-                    name=name, flavor_id=flavor_id, tenant_id=context.tenant,
1057
+                    name=name, flavor_id=flavor_id,
1058
+                    tenant_id=context.project_id,
1056 1059
                     volume_size=volume_size,
1057 1060
                     datastore_version_id=datastore_version.id,
1058 1061
                     task_status=InstanceTasks.BUILDING,
@@ -1062,7 +1065,7 @@ class Instance(BuiltInstance):
1062 1065
                     region_id=region_name)
1063 1066
                 LOG.debug("Tenant %(tenant)s created new Trove instance "
1064 1067
                           "%(db)s in region %(region)s.",
1065
-                          {'tenant': context.tenant, 'db': db_info.id,
1068
+                          {'tenant': context.project_id, 'db': db_info.id,
1066 1069
                            'region': region_name})
1067 1070
 
1068 1071
                 instance_id = db_info.id
@@ -1109,13 +1112,14 @@ class Instance(BuiltInstance):
1109 1112
                 volume_size, backup_id, availability_zone, root_password,
1110 1113
                 nics, overrides, slave_of_id, cluster_config,
1111 1114
                 volume_type=volume_type, modules=module_list,
1112
-                locality=locality)
1115
+                locality=locality, access=access)
1113 1116
 
1114 1117
             return SimpleInstance(context, db_info, service_status,
1115 1118
                                   root_password, locality=locality)
1116 1119
 
1117 1120
         with StartNotification(context, **call_args):
1118
-            return run_with_quotas(context.tenant, deltas, _create_resources)
1121
+            return run_with_quotas(context.project_id, deltas,
1122
+                                   _create_resources)
1119 1123
 
1120 1124
     @classmethod
1121 1125
     def add_instance_modules(cls, context, instance_id, modules):
@@ -1507,7 +1511,7 @@ class Instances(object):
1507 1511
             raise TypeError(_("Argument context not defined."))
1508 1512
         client = create_nova_client(context)
1509 1513
         servers = client.servers.list()
1510
-        query_opts = {'tenant_id': context.tenant,
1514
+        query_opts = {'tenant_id': context.project_id,
1511 1515
                       'deleted': False}
1512 1516
         if not include_clustered:
1513 1517
             query_opts['cluster_id'] = None
@@ -1731,7 +1735,7 @@ def module_instance_count(context, module_id, include_clustered=False):
1731 1735
     if not include_clustered:
1732 1736
         filters.append(DBInstance.cluster_id.is_(None))
1733 1737
     if not context.is_admin:
1734
-        filters.append(DBInstance.tenant_id == context.tenant)
1738
+        filters.append(DBInstance.tenant_id == context.project_id)
1735 1739
     query = query.group_by(module_models.DBInstanceModule.md5)
1736 1740
     query = query.add_columns(*columns)
1737 1741
     query = query.filter(*filters)

+ 8
- 4
trove/instance/service.py View File

@@ -249,8 +249,9 @@ class InstanceController(wsgi.Controller):
249 249
         server = models.load_instance_with_info(models.DetailInstance,
250 250
                                                 context, id)
251 251
         self.authorize_instance_action(context, 'show', server)
252
-        return wsgi.Result(views.InstanceDetailView(server,
253
-                                                    req=req).data(), 200)
252
+        return wsgi.Result(
253
+            views.InstanceDetailView(server, req=req).data(), 200
254
+        )
254 255
 
255 256
     def delete(self, req, tenant_id, id):
256 257
         """Delete a single instance."""
@@ -340,7 +341,7 @@ class InstanceController(wsgi.Controller):
340 341
             backup_id = None
341 342
 
342 343
         availability_zone = body['instance'].get('availability_zone')
343
-        nics = body['instance'].get('nics')
344
+        nics = body['instance'].get('nics', [])
344 345
 
345 346
         slave_of_id = body['instance'].get('replica_of',
346 347
                                            # also check for older name
@@ -360,7 +361,9 @@ class InstanceController(wsgi.Controller):
360 361
                     'Cannot specify locality when adding replicas to existing '
361 362
                     'master.')
362 363
                 raise exception.BadRequest(message=dupe_locality_msg)
364
+
363 365
         region_name = body['instance'].get('region_name', CONF.os_region_name)
366
+        access = body['instance'].get('access', None)
364 367
 
365 368
         instance = models.Instance.create(context, name, flavor_id,
366 369
                                           image_id, databases, users,
@@ -372,7 +375,8 @@ class InstanceController(wsgi.Controller):
372 375
                                           volume_type=volume_type,
373 376
                                           modules=modules,
374 377
                                           locality=locality,
375
-                                          region_name=region_name)
378
+                                          region_name=region_name,
379
+                                          access=access)
376 380
 
377 381
         view = views.InstanceDetailView(instance, req=req)
378 382
         return wsgi.Result(view.data(), 200)

+ 1
- 1
trove/instance/tasks.py View File

@@ -119,7 +119,7 @@ class InstanceTasks(object):
119 119
                                              'guestagent timeout.',
120 120
                                              is_error=True)
121 121
     BUILDING_ERROR_PORT = InstanceTask(0x5c, 'BUILDING',
122
-                                       'Build error: Management port.',
122
+                                       'Build error: Port.',
123 123
                                        is_error=True)
124 124
 
125 125
 # Dissuade further additions at run-time.

+ 10
- 7
trove/module/models.py View File

@@ -65,10 +65,12 @@ class Modules(object):
65 65
             # plus the 'all' tenant ones
66 66
             query_opts['visible'] = True
67 67
             db_info = DBModule.query().filter_by(**query_opts)
68
-            db_info = db_info.filter(or_(DBModule.tenant_id == context.tenant,
69
-                                         DBModule.tenant_id.is_(None)))
68
+            db_info = db_info.filter(
69
+                or_(DBModule.tenant_id == context.project_id,
70
+                    DBModule.tenant_id.is_(None))
71
+            )
70 72
             if db_info.count() == 0:
71
-                LOG.debug("No modules found for tenant %s", context.tenant)
73
+                LOG.debug("No modules found for tenant %s", context.project_id)
72 74
         modules = db_info.all()
73 75
         return modules
74 76
 
@@ -83,12 +85,12 @@ class Modules(object):
83 85
         query_opts = {'deleted': False,
84 86
                       'auto_apply': True}
85 87
         db_info = DBModule.query().filter_by(**query_opts)
86
-        db_info = Modules.add_tenant_filter(db_info, context.tenant)
88
+        db_info = Modules.add_tenant_filter(db_info, context.project_id)
87 89
         db_info = Modules.add_datastore_filter(db_info, datastore_id)
88 90
         db_info = Modules.add_ds_version_filter(db_info, datastore_version_id)
89 91
         if db_info.count() == 0:
90 92
             LOG.debug("No auto-apply modules found for tenant %s",
91
-                      context.tenant)
93
+                      context.project_id)
92 94
         modules = db_info.all()
93 95
         return modules
94 96
 
@@ -123,7 +125,8 @@ class Modules(object):
123 125
             query_opts = {'deleted': False}
124 126
             db_info = DBModule.query().filter_by(**query_opts)
125 127
             if not context.is_admin:
126
-                db_info = Modules.add_tenant_filter(db_info, context.tenant)
128
+                db_info = Modules.add_tenant_filter(db_info,
129
+                                                    context.project_id)
127 130
             db_info = db_info.filter(DBModule.id.in_(module_ids))
128 131
             modules = db_info.all()
129 132
         return modules
@@ -285,7 +288,7 @@ class Module(object):
285 288
                 module = DBModule.find_by(id=module_id, deleted=False)
286 289
             else:
287 290
                 module = DBModule.find_by(
288
-                    id=module_id, tenant_id=context.tenant, visible=True,
291
+                    id=module_id, tenant_id=context.project_id, visible=True,
289 292
                     deleted=False)
290 293
         except exception.ModelNotFoundError:
291 294
             # See if we have the module in the 'all' tenant section

+ 0
- 14
trove/network/base.py View File

@@ -28,24 +28,10 @@ class NetworkDriver(object):
28 28
         Returns security group with given group_id
29 29
         """
30 30
 
31
-    @abc.abstractmethod
32
-    def create_security_group(self, name, description):
33
-        """
34
-        Creates the security group with given name and description
35
-        """
36
-
37 31
     @abc.abstractmethod
38 32
     def delete_security_group(self, sec_group_id):
39 33
         """Deletes the security group by given ID."""
40 34
 
41
-    @abc.abstractmethod
42
-    def add_security_group_rule(self, sec_group_id, protocol,
43
-                                from_port, to_port, cidr):
44
-        """
45
-        Adds the rule identified by the security group ID,
46
-        transport protocol, port range: from -> to, CIDR.
47
-        """
48
-
49 35
     @abc.abstractmethod
50 36
     def delete_security_group_rule(self, sec_group_rule_id):
51 37
         """Deletes the rule by given ID."""

+ 0
- 91
trove/network/neutron.py View File

@@ -12,8 +12,6 @@
12 12
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 13
 #    License for the specific language governing permissions and limitations
14 14
 #    under the License.
15
-#
16
-
17 15
 from neutronclient.common import exceptions as neutron_exceptions
18 16
 from oslo_log import log as logging
19 17
 
@@ -21,23 +19,8 @@ from trove.common import exception
21 19
 from trove.common import remote
22 20
 from trove.network import base
23 21
 
24
-
25 22
 LOG = logging.getLogger(__name__)
26 23
 
27
-CONST = {'IPv4': "IPv4",
28
-         'IPv6': "IPv6",
29
-         'INGRESS': "ingress",
30
-         'EGRESS': "egress",
31
-         'PROTO_NAME_TCP': 'tcp',
32
-         'PROTO_NAME_ICMP': 'icmp',
33
-         'PROTO_NAME_ICMP_V6': 'icmpv6',
34
-         'PROTO_NAME_UDP': 'udp'}
35
-
36
-
37
-class NovaNetworkStruct(object):
38
-    def __init__(self, **properties):
39
-        self.__dict__.update(properties)
40
-
41 24
 
42 25
 class NeutronDriver(base.NetworkDriver):
43 26
 
@@ -54,18 +37,6 @@ class NeutronDriver(base.NetworkDriver):
54 37
             LOG.exception('Failed to get remote security group')
55 38
             raise exception.TroveError(str(e))
56 39
 
57
-    def create_security_group(self, name, description):
58
-        try:
59
-            sec_group_body = {"security_group": {"name": name,
60
-                                                 "description": description}}
61
-            sec_group = self.client.create_security_group(body=sec_group_body)
62
-            return self._convert_to_nova_security_group_format(
63
-                sec_group.get('security_group', sec_group))
64
-
65
-        except neutron_exceptions.NeutronClientException as e:
66
-            LOG.exception('Failed to create remote security group')
67
-            raise exception.SecurityGroupCreationError(str(e))
68
-
69 40
     def delete_security_group(self, sec_group_id):
70 41
         try:
71 42
             self.client.delete_security_group(security_group=sec_group_id)
@@ -73,34 +44,6 @@ class NeutronDriver(base.NetworkDriver):
73 44
             LOG.exception('Failed to delete remote security group')
74 45
             raise exception.SecurityGroupDeletionError(str(e))
75 46
 
76
-    def add_security_group_rule(self, sec_group_id, protocol,
77
-                                from_port, to_port, cidr,
78
-                                direction=CONST['INGRESS'],
79
-                                ethertype=CONST['IPv4']):
80
-        try:
81
-            secgroup_rule_body = {"security_group_rule":
82
-                                  {"security_group_id": sec_group_id,
83
-                                   "protocol": protocol,
84
-                                   "port_range_min": from_port,
85
-                                   "port_range_max": to_port,
86
-                                   "remote_ip_prefix": cidr,
87
-                                   "direction": direction,  # ingress | egress
88
-                                   "ethertype": ethertype,  # IPv4 | IPv6
89
-                                   }}
90
-
91
-            secgroup_rule = self.client.create_security_group_rule(
92
-                secgroup_rule_body)
93
-            return self._convert_to_nova_security_group_rule_format(
94
-                secgroup_rule.get('security_group_rule', secgroup_rule))
95
-        except neutron_exceptions.NeutronClientException as e:
96
-            # ignore error if rule already exists
97
-            if e.status_code == 409:
98
-                LOG.exception("Security group rule already exists")
99
-            else:
100
-                LOG.exception('Failed to add rule to remote security '
101
-                              'group')
102
-                raise exception.SecurityGroupRuleCreationError(str(e))
103
-
104 47
     def delete_security_group_rule(self, sec_group_rule_id):
105 48
         try:
106 49
             self.client.delete_security_group_rule(
@@ -109,37 +52,3 @@ class NeutronDriver(base.NetworkDriver):
109 52
         except neutron_exceptions.NeutronClientException as e:
110 53
             LOG.exception('Failed to delete rule to remote security group')
111 54
             raise exception.SecurityGroupRuleDeletionError(str(e))
112
-
113
-    def _convert_to_nova_security_group_format(self, security_group):
114
-        nova_group = {}
115
-        nova_group['id'] = security_group['id']
116
-        nova_group['description'] = security_group['description']
117
-        nova_group['name'] = security_group['name']
118
-        nova_group['project_id'] = security_group['tenant_id']
119
-        nova_group['rules'] = []
120
-        for rule in security_group.get('security_group_rules', []):
121
-            if rule['direction'] == 'ingress':
122
-                nova_group['rules'].append(
123
-                    self._convert_to_nova_security_group_rule_format(rule))
124
-
125
-        return NovaNetworkStruct(**nova_group)
126
-
127
-    def _convert_to_nova_security_group_rule_format(self, rule):
128
-        nova_rule = {}
129
-        nova_rule['id'] = rule['id']
130
-        nova_rule['parent_group_id'] = rule['security_group_id']
131
-        nova_rule['protocol'] = rule['protocol']
132
-        if (nova_rule['protocol'] and rule.get('port_range_min') is None and
133
-                rule.get('port_range_max') is None):
134
-            if rule['protocol'].upper() in ['TCP', 'UDP']:
135
-                nova_rule['from_port'] = 1
136
-                nova_rule['to_port'] = 65535
137
-            else:
138
-                nova_rule['from_port'] = -1
139
-                nova_rule['to_port'] = -1
140
-        else:
141
-            nova_rule['from_port'] = rule.get('port_range_min')
142
-            nova_rule['to_port'] = rule.get('port_range_max')
143
-        nova_rule['group_id'] = rule['remote_group_id']
144
-        nova_rule['cidr'] = rule.get('remote_ip_prefix')
145
-        return NovaNetworkStruct(**nova_rule)

+ 0
- 25
trove/network/nova.py View File

@@ -21,7 +21,6 @@ from trove.common import exception
21 21
 from trove.common import remote
22 22
 from trove.network import base
23 23
 
24
-
25 24
 LOG = logging.getLogger(__name__)
26 25
 
27 26
 
@@ -41,15 +40,6 @@ class NovaNetwork(base.NetworkDriver):
41 40
             LOG.exception('Failed to get remote security group')
42 41
             raise exception.TroveError(str(e))
43 42
 
44
-    def create_security_group(self, name, description):
45
-        try:
46
-            sec_group = self.client.security_groups.create(
47
-                name=name, description=description)
48
-            return sec_group
49
-        except nova_exceptions.ClientException as e:
50
-            LOG.exception('Failed to create remote security group')
51
-            raise exception.SecurityGroupCreationError(str(e))
52
-
53 43
     def delete_security_group(self, sec_group_id):
54 44
         try:
55 45
             self.client.security_groups.delete(sec_group_id)
@@ -57,21 +47,6 @@ class NovaNetwork(base.NetworkDriver):
57 47
             LOG.exception('Failed to delete remote security group')
58 48
             raise exception.SecurityGroupDeletionError(str(e))
59 49
 
60
-    def add_security_group_rule(self, sec_group_id, protocol,
61
-                                from_port, to_port, cidr):
62
-        try:
63
-            sec_group_rule = self.client.security_group_rules.create(
64
-                parent_group_id=sec_group_id,
65
-                ip_protocol=protocol,
66
-                from_port=from_port,
67
-                to_port=to_port,
68
-                cidr=cidr)
69
-
70
-            return sec_group_rule
71
-        except nova_exceptions.ClientException as e:
72
-            LOG.exception('Failed to add rule to remote security group')
73
-            raise exception.SecurityGroupRuleCreationError(str(e))
74
-
75 50
     def delete_security_group_rule(self, sec_group_rule_id):
76 51
         try:
77 52
             self.client.security_group_rules.delete(sec_group_rule_id)

+ 2
- 2
trove/taskmanager/api.py View File

@@ -193,7 +193,7 @@ class API(object):
193 193
                         availability_zone=None, root_password=None,
194 194
                         nics=None, overrides=None, slave_of_id=None,
195 195
                         cluster_config=None, volume_type=None,
196
-                        modules=None, locality=None):
196
+                        modules=None, locality=None, access=None):
197 197
 
198 198
         LOG.debug("Making async call to create instance %s ", instance_id)
199 199
         version = self.API_BASE_VERSION
@@ -214,7 +214,7 @@ class API(object):
214 214
                    slave_of_id=slave_of_id,
215 215
                    cluster_config=cluster_config,
216 216
                    volume_type=volume_type,
217
-                   modules=modules, locality=locality)
217
+                   modules=modules, locality=locality, access=access)
218 218
 
219 219
     def create_cluster(self, cluster_id):
220 220
         LOG.debug("Making async call to create cluster %s ", cluster_id)

+ 23
- 13
trove/taskmanager/manager.py View File

@@ -330,7 +330,8 @@ class Manager(periodic_task.PeriodicTasks):
330 330
         master_instance_tasks = BuiltInstanceTasks.load(context, slave_of_id)
331 331
         server_group = master_instance_tasks.server_group
332 332
         scheduler_hints = srv_grp.ServerGroup.convert_to_hint(server_group)
333
-        LOG.debug("Using scheduler hints for locality: %s", scheduler_hints)
333
+        LOG.info("Using scheduler hints %s for creating instance %s",
334
+                 scheduler_hints, instance_id)
334 335
 
335 336
         try:
336 337
             for replica_index in range(0, len(ids)):
@@ -371,7 +372,8 @@ class Manager(periodic_task.PeriodicTasks):
371 372
                          image_id, databases, users, datastore_manager,
372 373
                          packages, volume_size, backup_id, availability_zone,
373 374
                          root_password, nics, overrides, slave_of_id,
374
-                         cluster_config, volume_type, modules, locality):
375
+                         cluster_config, volume_type, modules, locality,
376
+                         access=None):
375 377
         if slave_of_id:
376 378
             self._create_replication_slave(context, instance_id, name,
377 379
                                            flavor, image_id, databases, users,
@@ -384,17 +386,24 @@ class Manager(periodic_task.PeriodicTasks):
384 386
             if type(instance_id) in [list]:
385 387
                 raise AttributeError(_(
386 388
                     "Cannot create multiple non-replica instances."))
387
-            instance_tasks = FreshInstanceTasks.load(context, instance_id)
388 389
 
389 390
             scheduler_hints = srv_grp.ServerGroup.build_scheduler_hint(
390
-                context, locality, instance_id)
391
-            instance_tasks.create_instance(flavor, image_id, databases, users,
392
-                                           datastore_manager, packages,
393
-                                           volume_size, backup_id,
394
-                                           availability_zone, root_password,
395
-                                           nics, overrides, cluster_config,
396
-                                           None, volume_type, modules,
397
-                                           scheduler_hints)
391
+                context, locality, instance_id
392
+            )
393
+            LOG.info("Using scheduler hints %s for creating instance %s",
394
+                     scheduler_hints, instance_id)
395
+
396
+            instance_tasks = FreshInstanceTasks.load(context, instance_id)
397
+            instance_tasks.create_instance(
398
+                flavor, image_id, databases, users,
399
+                datastore_manager, packages,
400
+                volume_size, backup_id,
401
+                availability_zone, root_password,
402
+                nics, overrides, cluster_config,
403
+                None, volume_type, modules,
404
+                scheduler_hints, access=access
405
+            )
406
+
398 407
             timeout = (CONF.restore_usage_timeout if backup_id
399 408
                        else CONF.usage_timeout)
400 409
             instance_tasks.wait_for_instance(timeout, flavor)
@@ -403,7 +412,8 @@ class Manager(periodic_task.PeriodicTasks):
403 412
                         image_id, databases, users, datastore_manager,
404 413
                         packages, volume_size, backup_id, availability_zone,
405 414
                         root_password, nics, overrides, slave_of_id,
406
-                        cluster_config, volume_type, modules, locality):
415
+                        cluster_config, volume_type, modules, locality,
416
+                        access=None):
407 417
         with EndNotification(context,
408 418
                              instance_id=(instance_id[0]
409 419
                                           if isinstance(instance_id, list)
@@ -414,7 +424,7 @@ class Manager(periodic_task.PeriodicTasks):
414 424
                                   backup_id, availability_zone,
415 425
                                   root_password, nics, overrides, slave_of_id,
416 426
                                   cluster_config, volume_type, modules,
417
-                                  locality)
427
+                                  locality, access=access)
418 428
 
419 429
     def upgrade(self, context, instance_id, datastore_version_id):
420 430
         instance_tasks = models.BuiltInstanceTasks.load(context, instance_id)

+ 197
- 160
trove/taskmanager/models.py View File

@@ -12,6 +12,7 @@
12 12
 #    License for the specific language governing permissions and limitations
13 13
 #    under the License.
14 14
 
15
+import copy
15 16
 import os.path
16 17
 import time
17 18
 import traceback
@@ -39,13 +40,13 @@ from trove.common.exception import BackupCreationError
39 40
 from trove.common.exception import GuestError
40 41
 from trove.common.exception import GuestTimeout
41 42
 from trove.common.exception import InvalidModelError
42
-from trove.common.exception import MalformedSecurityGroupRuleError
43 43
 from trove.common.exception import PollTimeOut
44 44
 from trove.common.exception import TroveError
45 45
 from trove.common.exception import VolumeCreationFailure
46 46
 from trove.common.i18n import _
47 47
 from trove.common import instance as rd_instance
48 48
 from trove.common.instance import ServiceStatuses
49
+from trove.common import neutron
49 50
 from trove.common.notification import (
50 51
     DBaaSInstanceRestart,
51 52
     DBaaSInstanceUpgrade,
@@ -59,7 +60,6 @@ import trove.common.remote as remote
59 60
 from trove.common.remote import create_cinder_client
60 61
 from trove.common.remote import create_dns_client
61 62
 from trove.common.remote import create_guest_client
62
-from trove.common.remote import create_neutron_client
63 63
 from trove.common import server_group as srv_grp
64 64
 from trove.common.strategies.cluster import strategy
65 65
 from trove.common import template
@@ -67,8 +67,6 @@ from trove.common import timeutils
67 67
 from trove.common import utils
68 68
 from trove.common.utils import try_recover
69 69
 from trove.extensions.mysql import models as mysql_models
70
-from trove.extensions.security_group.models import SecurityGroup
71
-from trove.extensions.security_group.models import SecurityGroupRule
72 70
 from trove.instance import models as inst_models
73 71
 from trove.instance.models import BuiltInstance
74 72
 from trove.instance.models import DBInstance
@@ -425,15 +423,12 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
425 423
         # If volume has "available" status, delete it manually.
426 424
         try:
427 425
             if self.volume_id:
428
-                volume_client = create_cinder_client(self.context)
429
-                volume = volume_client.volumes.get(self.volume_id)
426
+                volume = self.volume_client.volumes.get(self.volume_id)
430 427
                 if volume.status == "available":
431
-                    LOG.info("Deleting volume %(v)s for instance: %(i)s.",
432
-                             {'v': self.volume_id, 'i': self.id})
433 428
                     volume.delete()
434
-        except Exception:
435
-            LOG.exception("Error deleting volume of instance %(id)s.",
436
-                          {'id': self.db_info.id})
429
+        except Exception as e:
430
+            LOG.warning("Failed to delete volume for instance %s, error: %s",
431
+                        self.id, six.text_type(e))
437 432
 
438 433
         LOG.debug("End _delete_resource for instance %s", self.id)
439 434
 
@@ -466,83 +461,116 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
466 461
                     self.id, error_message, error_details,
467 462
                     skip_delta=CONF.usage_sleep_time + 1)
468 463
 
469
-    def _create_management_port(self, network, default_sgs=[]):
470
-        """Create port in the management network."""
471
-        security_groups = default_sgs
472
-        if len(CONF.management_security_groups) > 0:
473
-            security_groups = CONF.management_security_groups
464
+    def _create_port(self, network, security_groups, is_mgmt=False,
465
+                     is_public=False):
466
+        name = 'trove-%s' % self.id
467
+        type = 'Management' if is_mgmt else 'User'
468
+        description = '%s port for trove instance %s' % (type, self.id)
474 469
 
475 470
         try:
476
-            neutron_client = create_neutron_client(self.context)
477
-
478
-            body = {
479
-                'port': {
480
-                    'name': 'trove-%s' % self.id,
481
-                    'description': ('Management port for Trove instance %s'
482
-                                    % self.id),
483
-                    'network_id': network,
484
-                    'admin_state_up': True,
485
-                    'security_groups': security_groups
486
-                }
487
-            }
488
-            port = neutron_client.create_port(body)
489
-            return port['port']['id']
471
+            port_id = neutron.create_port(
472
+                self.neutron_client, name,
473
+                description, network,
474
+                security_groups,
475
+                is_public=is_public
476
+            )
490 477
         except Exception:
491
-            error = "Failed to create management port."
478
+            error = ("Failed to create %s port for instance %s"
479
+                     % (type, self.id))
492 480
             LOG.exception(error)
493 481
             self.update_db(
494 482
                 task_status=inst_models.InstanceTasks.BUILDING_ERROR_PORT
495 483
             )
496 484
             raise TroveError(message=error)
497 485
 
486
+        return port_id
487
+
488
+    def _prepare_networks_for_instance(self, datastore_manager, nics,
489
+                                       access=None):
490
+        """Prepare the networks for the trove instance.
491
+
492
+        the params are all passed from trove-taskmanager.
493
+
494
+        Exception is raised if any error happens.