)]}'
{"nova/api/openstack/compute/quota_classes.py":[{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"eae0922f9e150551a01d26b6ee029776a80f80d3","unresolved":false,"context_lines":[{"line_number":83,"context_line":""},{"line_number":84,"context_line":"        for key, value in body[\u0027quota_class_set\u0027].items():"},{"line_number":85,"context_line":"            try:"},{"line_number":86,"context_line":"                objects.Quotas.update_class(context, quota_class, key, value)"},{"line_number":87,"context_line":"            except exception.QuotaClassNotFound:"},{"line_number":88,"context_line":"                objects.Quotas.create_class(context, quota_class, key, value)"},{"line_number":89,"context_line":""}],"source_content_type":"text/x-python","patch_set":16,"id":"1a1ced50_d2aa2370","line":86,"updated":"2017-03-17 14:34:20.000000000","message":"I would be tempted to split this out into a separate patch, before the API preferred move.","commit_id":"2c77c637db83ff8cea0bdc97042bf672d8eae2cb"}],"nova/cmd/manage.py":[{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"afa7cf7685e1eaad0d7edc5e3b32347b70efaf5f","unresolved":false,"context_lines":[{"line_number":207,"context_line":"            help\u003d\u0027User name\u0027)"},{"line_number":208,"context_line":"    @args(\u0027--key\u0027, metavar\u003d\u0027\u003ckey\u003e\u0027, help\u003d\u0027Key\u0027)"},{"line_number":209,"context_line":"    @args(\u0027--value\u0027, metavar\u003d\u0027\u003cvalue\u003e\u0027, help\u003d\u0027Value\u0027)"},{"line_number":210,"context_line":"    def quota(self, project_id, user_id\u003dNone, key\u003dNone, value\u003dNone):"},{"line_number":211,"context_line":"        \"\"\"Create, update or display quotas for project/user"},{"line_number":212,"context_line":""},{"line_number":213,"context_line":"        If no quota key is provided, the quota will be displayed."}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_a19b4bc8","line":210,"updated":"2017-01-20 13:54:07.000000000","message":"Oh dear, I never new this existed. We should kill this. But thats obviously another change.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"}],"nova/objects/quotas.py":[{"author":{"_account_id":21813,"name":"Andrey Volkov","email":"m@amadev.ru","username":"avolkov"},"change_message_id":"8e73ac0f1f3207b2464ae00cd274a7733d0ea1c8","unresolved":false,"context_lines":[{"line_number":73,"context_line":""},{"line_number":74,"context_line":"    @staticmethod"},{"line_number":75,"context_line":"    @db_api.api_context_manager.reader"},{"line_number":76,"context_line":"    def _get_from_db(context, project_id, resource, user_id\u003dNone):"},{"line_number":77,"context_line":"        model \u003d api_models.ProjectUserQuota if user_id else api_models.Quota"},{"line_number":78,"context_line":"        query \u003d context.session.query(model).\\"},{"line_number":79,"context_line":"                        filter_by(project_id\u003dproject_id).\\"}],"source_content_type":"text/x-python","patch_set":2,"id":"da4df55a_2a063386","line":76,"updated":"2016-12-26 11:52:08.000000000","message":"With the case with the methods copying do we need some tests that behavior wasn\u0027t changed?\n\nTheoretically, we can change db.sqlalchemy api functions so they can be used with api_models.","commit_id":"c7aba13b52c67be3a2a37be1fec999ea576ca562"},{"author":{"_account_id":21813,"name":"Andrey Volkov","email":"m@amadev.ru","username":"avolkov"},"change_message_id":"8e73ac0f1f3207b2464ae00cd274a7733d0ea1c8","unresolved":false,"context_lines":[{"line_number":75,"context_line":"    @db_api.api_context_manager.reader"},{"line_number":76,"context_line":"    def _get_from_db(context, project_id, resource, user_id\u003dNone):"},{"line_number":77,"context_line":"        model \u003d api_models.ProjectUserQuota if user_id else api_models.Quota"},{"line_number":78,"context_line":"        query \u003d context.session.query(model).\\"},{"line_number":79,"context_line":"                        filter_by(project_id\u003dproject_id).\\"},{"line_number":80,"context_line":"                        filter_by(resource\u003dresource)"},{"line_number":81,"context_line":"        if user_id:"}],"source_content_type":"text/x-python","patch_set":2,"id":"da4df55a_042b2895","line":78,"updated":"2016-12-26 11:52:08.000000000","message":"From the structure of the db directory in nova I see there are\ndb.api and sqlalchemy implementation for that. Do we want to refuse such abstraction and use sqlalchemy by default? Or we can use both ...\n\nAlso, we can miss some logic from model_query here.","commit_id":"c7aba13b52c67be3a2a37be1fec999ea576ca562"},{"author":{"_account_id":21813,"name":"Andrey Volkov","email":"m@amadev.ru","username":"avolkov"},"change_message_id":"8e73ac0f1f3207b2464ae00cd274a7733d0ea1c8","unresolved":false,"context_lines":[{"line_number":343,"context_line":"    @base.remotable_classmethod"},{"line_number":344,"context_line":"    def get_all_by_project(cls, context, project_id):"},{"line_number":345,"context_line":"        try:"},{"line_number":346,"context_line":"            quota \u003d cls._get_all_from_db_by_project(context, project_id)"},{"line_number":347,"context_line":"        except exception.ProjectQuotaNotFound:"},{"line_number":348,"context_line":"            quota \u003d db.quota_get_all_by_project(context, project_id)"},{"line_number":349,"context_line":"        return quota"}],"source_content_type":"text/x-python","patch_set":2,"id":"da4df55a_eae34ba2","line":346,"updated":"2016-12-26 11:52:08.000000000","message":"Without quotas we do two queries. Did you consider to make some label for migration is done?","commit_id":"c7aba13b52c67be3a2a37be1fec999ea576ca562"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"b8b1073f5ec5e7c260e31a033f29a5bd21096c23","unresolved":false,"context_lines":[{"line_number":78,"context_line":""},{"line_number":79,"context_line":"    @staticmethod"},{"line_number":80,"context_line":"    @db_api.api_context_manager.reader"},{"line_number":81,"context_line":"    def _get_from_db(context, project_id, resource, user_id\u003dNone):"},{"line_number":82,"context_line":"        model \u003d api_models.ProjectUserQuota if user_id else api_models.Quota"},{"line_number":83,"context_line":"        query \u003d context.session.query(model).\\"},{"line_number":84,"context_line":"                        filter_by(project_id\u003dproject_id).\\"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_ecf9c781","line":81,"updated":"2017-01-20 16:01:22.000000000","message":"Feels like two methods really, but I see these are all really a directly copy of the old ones for the compute db, which makes sense for this transition.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"b8b1073f5ec5e7c260e31a033f29a5bd21096c23","unresolved":false,"context_lines":[{"line_number":108,"context_line":"        rows \u003d context.session.query(api_models.Quota).\\"},{"line_number":109,"context_line":"                        filter_by(project_id\u003dproject_id).\\"},{"line_number":110,"context_line":"                        all()"},{"line_number":111,"context_line":"        result \u003d {\u0027project_id\u0027: project_id}"},{"line_number":112,"context_line":"        for row in rows:"},{"line_number":113,"context_line":"            result[row.resource] \u003d row.hard_limit"},{"line_number":114,"context_line":"        return result"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_2c92ef9e","line":111,"updated":"2017-01-20 16:01:22.000000000","message":"FWIW, returning \u0027user_id\u0027: None in here, is where I starting thinking about collapsing those two tables.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"b8b1073f5ec5e7c260e31a033f29a5bd21096c23","unresolved":false,"context_lines":[{"line_number":183,"context_line":"        # is removed."},{"line_number":184,"context_line":"        per_user \u003d user_id and resource not in db_api.PER_PROJECT_QUOTAS"},{"line_number":185,"context_line":"        quota_ref \u003d (api_models.ProjectUserQuota() if per_user"},{"line_number":186,"context_line":"                     else api_models.Quota())"},{"line_number":187,"context_line":"        if per_user:"},{"line_number":188,"context_line":"            quota_ref.user_id \u003d user_id"},{"line_number":189,"context_line":"        quota_ref.project_id \u003d project_id"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_d24021a8","line":186,"updated":"2017-01-20 16:01:22.000000000","message":"Note really related to this patch, just an observation...\n\nI think this means we have all the per user ones in ProjectUserQuota, and all the user_id\u003dNone ones in Quota.\n\nBut for the PER_PROJECT_QUOTAS, they are always forced to be in Quota, as we ignore any per user quotas for those ones.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"21b025fcf6391b7180aeb6396f8accb9dfbb7681","unresolved":false,"context_lines":[{"line_number":183,"context_line":"        # is removed."},{"line_number":184,"context_line":"        per_user \u003d user_id and resource not in db_api.PER_PROJECT_QUOTAS"},{"line_number":185,"context_line":"        quota_ref \u003d (api_models.ProjectUserQuota() if per_user"},{"line_number":186,"context_line":"                     else api_models.Quota())"},{"line_number":187,"context_line":"        if per_user:"},{"line_number":188,"context_line":"            quota_ref.user_id \u003d user_id"},{"line_number":189,"context_line":"        quota_ref.project_id \u003d project_id"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_e1f9c581","line":186,"in_reply_to":"5a3905b3_a6a6f30d","updated":"2017-01-20 17:35:01.000000000","message":"I was thinking that if we are left with the two tables with half the data in one and half in the other, after we remove nova-net, we drop the per-project table and all the data in it, because we don\u0027t need the fixed_ips, floating_ips, networks quota limit data anymore.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"88465e698115fa5c984e5bfc9cba6342200b90e3","unresolved":false,"context_lines":[{"line_number":183,"context_line":"        # is removed."},{"line_number":184,"context_line":"        per_user \u003d user_id and resource not in db_api.PER_PROJECT_QUOTAS"},{"line_number":185,"context_line":"        quota_ref \u003d (api_models.ProjectUserQuota() if per_user"},{"line_number":186,"context_line":"                     else api_models.Quota())"},{"line_number":187,"context_line":"        if per_user:"},{"line_number":188,"context_line":"            quota_ref.user_id \u003d user_id"},{"line_number":189,"context_line":"        quota_ref.project_id \u003d project_id"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_a6a6f30d","line":186,"in_reply_to":"5a3905b3_ab807c47","updated":"2017-01-20 17:10:42.000000000","message":"So I think its worse, as currently all user quotas go in one table (there are a bunch of things that aren\u0027t allowed user quotas - the nova-net stuff), and all the project quotas go in the other.\n\nSo when we drop nova-net, I think we are still left with two tables with half the data in one, and half in the other.\n\nHonestly, we can leave this for another time, this feels so close now, and complicated enough, that it not being a direct copy is going to be more confusing.\n\nOnce its all in the API, using your new method, we can think about another move for the tidy up. That should be simpler.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"4780c86c934608a3c871aede02a40598107af3ae","unresolved":false,"context_lines":[{"line_number":183,"context_line":"        # is removed."},{"line_number":184,"context_line":"        per_user \u003d user_id and resource not in db_api.PER_PROJECT_QUOTAS"},{"line_number":185,"context_line":"        quota_ref \u003d (api_models.ProjectUserQuota() if per_user"},{"line_number":186,"context_line":"                     else api_models.Quota())"},{"line_number":187,"context_line":"        if per_user:"},{"line_number":188,"context_line":"            quota_ref.user_id \u003d user_id"},{"line_number":189,"context_line":"        quota_ref.project_id \u003d project_id"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_ab807c47","line":186,"in_reply_to":"5a3905b3_d24021a8","updated":"2017-01-20 16:58:57.000000000","message":"Yes, that\u0027s right. I can\u0027t think of a reason it was done this way other than to have a really clear separation of project-scoped quotas and user-scoped quotas. That is, instead of filtering on user_id\u003dNULL, you just go to one table or the other. Edit: After thinking more, I think that user-scoped quotas added later after project-scoped already existed. At the beginning, quotas could only be scoped per project.\n\nI didn\u0027t consider collapsing them together, though I think they could be. After nova-network is removed, if quotas are kept separate, we would just drop the Quotas table instead of doing an online migration that deletes all of the quota limits where user_id\u003dNULL. The only non-ideal thing there would be that the remaining table will be \u0027project_user_quotas\u0027 instead of the more straightforward table name \u0027quotas\u0027.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"851840a554e43f19208251ceab7f319206806cf0","unresolved":false,"context_lines":[{"line_number":183,"context_line":"        # is removed."},{"line_number":184,"context_line":"        per_user \u003d user_id and resource not in db_api.PER_PROJECT_QUOTAS"},{"line_number":185,"context_line":"        quota_ref \u003d (api_models.ProjectUserQuota() if per_user"},{"line_number":186,"context_line":"                     else api_models.Quota())"},{"line_number":187,"context_line":"        if per_user:"},{"line_number":188,"context_line":"            quota_ref.user_id \u003d user_id"},{"line_number":189,"context_line":"        quota_ref.project_id \u003d project_id"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_f9ce9e2b","line":186,"in_reply_to":"5a3905b3_e1f9c581","updated":"2017-01-23 11:14:51.000000000","message":"I don\u0027t think it works like that though. Its just the nova-network stuff can\u0027t be set per user, everything else can be set per project or per user, and into different tables. I think?","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"b8b1073f5ec5e7c260e31a033f29a5bd21096c23","unresolved":false,"context_lines":[{"line_number":201,"context_line":"    def _update_limit_in_db(context, project_id, resource, limit,"},{"line_number":202,"context_line":"                            user_id\u003dNone):"},{"line_number":203,"context_line":"        # TODO(melwitt): We won\u0027t need PER_PROJECT_QUOTAS after nova-network"},{"line_number":204,"context_line":"        # is removed."},{"line_number":205,"context_line":"        per_user \u003d user_id and resource not in db_api.PER_PROJECT_QUOTAS"},{"line_number":206,"context_line":"        model \u003d api_models.ProjectUserQuota if per_user else api_models.Quota"},{"line_number":207,"context_line":"        query \u003d context.session.query(model).\\"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_8f3bd59b","line":204,"updated":"2017-01-20 16:01:22.000000000","message":"Very good point.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"e3ae2c170d477857d76628f9e6a36f1794e21678","unresolved":false,"context_lines":[{"line_number":225,"context_line":"        quota_class_ref.class_name \u003d class_name"},{"line_number":226,"context_line":"        quota_class_ref.resource \u003d resource"},{"line_number":227,"context_line":"        quota_class_ref.hard_limit \u003d limit"},{"line_number":228,"context_line":"        quota_class_ref.save(context.session)"},{"line_number":229,"context_line":"        return quota_class_ref"},{"line_number":230,"context_line":""},{"line_number":231,"context_line":"    @staticmethod"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_cd2e45a4","line":228,"updated":"2017-01-24 09:50:44.000000000","message":"Should we not handle DBDuplicateEntry here?","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"e3ae2c170d477857d76628f9e6a36f1794e21678","unresolved":false,"context_lines":[{"line_number":304,"context_line":"        # NOTE(danms,comstud): Quotas likely needs an overhaul and currently"},{"line_number":305,"context_line":"        # doesn\u0027t map very well to objects. Since there is quite a bit of"},{"line_number":306,"context_line":"        # logic in the db api layer for this, just duplicate it for now."},{"line_number":307,"context_line":"        cls._create_limit_in_db(context, project_id, resource, limit,"},{"line_number":308,"context_line":"                                user_id\u003duser_id)"},{"line_number":309,"context_line":""},{"line_number":310,"context_line":"    @base.remotable_classmethod"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_2df42959","line":307,"updated":"2017-01-24 09:50:44.000000000","message":"What we need here is to first check if this exists in the old DB already, if it does, we should error out:\n\n   raise exception.QuotaExists(project_id\u003dproject_id, resource\u003dresource)\n\nSee:\nhttps://github.com/openstack/nova/blob/c9eb9530314d047f5013941ebcfd5ef0192a9dc3/nova/db/sqlalchemy/api.py#L3448\n\nThat should stop the duplicates.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"525b04d43f4575b8759c9dd144b01a761a22037e","unresolved":false,"context_lines":[{"line_number":304,"context_line":"        # NOTE(danms,comstud): Quotas likely needs an overhaul and currently"},{"line_number":305,"context_line":"        # doesn\u0027t map very well to objects. Since there is quite a bit of"},{"line_number":306,"context_line":"        # logic in the db api layer for this, just duplicate it for now."},{"line_number":307,"context_line":"        cls._create_limit_in_db(context, project_id, resource, limit,"},{"line_number":308,"context_line":"                                user_id\u003duser_id)"},{"line_number":309,"context_line":""},{"line_number":310,"context_line":"    @base.remotable_classmethod"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_bbbe8562","line":307,"in_reply_to":"5a3905b3_2df42959","updated":"2017-01-24 14:38:39.000000000","message":"I was thinking the same thing -- I think I mentioned it earlier in one of my long rambles. Thanks for pointing out the example, I\u0027ll update this to check for dupes in the old DB.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"b8b1073f5ec5e7c260e31a033f29a5bd21096c23","unresolved":false,"context_lines":[{"line_number":333,"context_line":"    def get_all(cls, context, project_id):"},{"line_number":334,"context_line":"        api_db_quotas \u003d cls._get_all_from_db(context, project_id)"},{"line_number":335,"context_line":"        main_db_quotas \u003d db.quota_get_all(context, project_id)"},{"line_number":336,"context_line":"        return api_db_quotas + main_db_quotas"},{"line_number":337,"context_line":""},{"line_number":338,"context_line":"    @base.remotable_classmethod"},{"line_number":339,"context_line":"    def get_all_by_project(cls, context, project_id):"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_d2166654","line":336,"updated":"2017-01-20 16:01:22.000000000","message":"I guess we could have duplicates in here too?","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"88465e698115fa5c984e5bfc9cba6342200b90e3","unresolved":false,"context_lines":[{"line_number":333,"context_line":"    def get_all(cls, context, project_id):"},{"line_number":334,"context_line":"        api_db_quotas \u003d cls._get_all_from_db(context, project_id)"},{"line_number":335,"context_line":"        main_db_quotas \u003d db.quota_get_all(context, project_id)"},{"line_number":336,"context_line":"        return api_db_quotas + main_db_quotas"},{"line_number":337,"context_line":""},{"line_number":338,"context_line":"    @base.remotable_classmethod"},{"line_number":339,"context_line":"    def get_all_by_project(cls, context, project_id):"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_2b7e4cae","line":336,"in_reply_to":"5a3905b3_d2166654","updated":"2017-01-20 17:10:42.000000000","message":"I see now this is really quite an edge case, we only get duplicate during the migration.\n\nProbably should filter this one though.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"b8b1073f5ec5e7c260e31a033f29a5bd21096c23","unresolved":false,"context_lines":[{"line_number":340,"context_line":"        api_db_quotas_dict \u003d cls._get_all_from_db_by_project(context,"},{"line_number":341,"context_line":"                                                             project_id)"},{"line_number":342,"context_line":"        main_db_quotas_dict \u003d db.quota_get_all_by_project(context, project_id)"},{"line_number":343,"context_line":"        # If any keys are duplicated, favor the API key during merge."},{"line_number":344,"context_line":"        for k, v in api_db_quotas_dict.items():"},{"line_number":345,"context_line":"            main_db_quotas_dict[k] \u003d v"},{"line_number":346,"context_line":"        return main_db_quotas_dict"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_6317a2ed","line":343,"updated":"2017-01-20 16:01:22.000000000","message":"I wonder if we should delete any duplicates when we find them? Thats probably too disruptive.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"4780c86c934608a3c871aede02a40598107af3ae","unresolved":false,"context_lines":[{"line_number":340,"context_line":"        api_db_quotas_dict \u003d cls._get_all_from_db_by_project(context,"},{"line_number":341,"context_line":"                                                             project_id)"},{"line_number":342,"context_line":"        main_db_quotas_dict \u003d db.quota_get_all_by_project(context, project_id)"},{"line_number":343,"context_line":"        # If any keys are duplicated, favor the API key during merge."},{"line_number":344,"context_line":"        for k, v in api_db_quotas_dict.items():"},{"line_number":345,"context_line":"            main_db_quotas_dict[k] \u003d v"},{"line_number":346,"context_line":"        return main_db_quotas_dict"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_8baca0a1","line":343,"in_reply_to":"5a3905b3_6317a2ed","updated":"2017-01-20 16:58:57.000000000","message":"The scenario I was thinking of for a duplicate is, since creates are in the API DB only, what if someone created a quota limit that already exists in the main DB? (I don\u0027t think it makes sense to do this, but someone could do it). Then, if they called \u0027get_all\u0027 there would be two limits for the same resource: one from the API DB and one from the main DB. The only way around that would be to first check the main DB and only create if not in main DB. I\u0027m wondering if maybe we have to do that, else what will happen during the online migration? It would skip the migration because of a duplicate (if DBDuplicateEntry handling is added). It would probably be best to do the check before create and avoid duplicates altogether.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"11c349cf706dc8d5362048a53d07be4214b11011","unresolved":false,"context_lines":[{"line_number":340,"context_line":"        api_db_quotas_dict \u003d cls._get_all_from_db_by_project(context,"},{"line_number":341,"context_line":"                                                             project_id)"},{"line_number":342,"context_line":"        main_db_quotas_dict \u003d db.quota_get_all_by_project(context, project_id)"},{"line_number":343,"context_line":"        # If any keys are duplicated, favor the API key during merge."},{"line_number":344,"context_line":"        for k, v in api_db_quotas_dict.items():"},{"line_number":345,"context_line":"            main_db_quotas_dict[k] \u003d v"},{"line_number":346,"context_line":"        return main_db_quotas_dict"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_593ed228","line":343,"in_reply_to":"5a3905b3_86081793","updated":"2017-01-23 11:13:11.000000000","message":"Oh dam, you are correct:\nhttps://github.com/openstack/nova/blob/master/nova/api/openstack/compute/quota_sets.py#L175\n\nWe don\u0027t get the exception on the create, so we end up with two. Then the sync logic can hit duplicates.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"88465e698115fa5c984e5bfc9cba6342200b90e3","unresolved":false,"context_lines":[{"line_number":340,"context_line":"        api_db_quotas_dict \u003d cls._get_all_from_db_by_project(context,"},{"line_number":341,"context_line":"                                                             project_id)"},{"line_number":342,"context_line":"        main_db_quotas_dict \u003d db.quota_get_all_by_project(context, project_id)"},{"line_number":343,"context_line":"        # If any keys are duplicated, favor the API key during merge."},{"line_number":344,"context_line":"        for k, v in api_db_quotas_dict.items():"},{"line_number":345,"context_line":"            main_db_quotas_dict[k] \u003d v"},{"line_number":346,"context_line":"        return main_db_quotas_dict"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_c60dbf38","line":343,"in_reply_to":"5a3905b3_8baca0a1","updated":"2017-01-20 17:10:42.000000000","message":"Yeah, been re-thinking this one, the duplicates would only occur due to races in the online migrations, lets leave that script to sort out the mess.\n\nThat means here we only need to worry about duplicates on fetch.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"4ff637761caeb318a283519b33261620013cefc6","unresolved":false,"context_lines":[{"line_number":340,"context_line":"        api_db_quotas_dict \u003d cls._get_all_from_db_by_project(context,"},{"line_number":341,"context_line":"                                                             project_id)"},{"line_number":342,"context_line":"        main_db_quotas_dict \u003d db.quota_get_all_by_project(context, project_id)"},{"line_number":343,"context_line":"        # If any keys are duplicated, favor the API key during merge."},{"line_number":344,"context_line":"        for k, v in api_db_quotas_dict.items():"},{"line_number":345,"context_line":"            main_db_quotas_dict[k] \u003d v"},{"line_number":346,"context_line":"        return main_db_quotas_dict"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_86081793","line":343,"in_reply_to":"5a3905b3_c60dbf38","updated":"2017-01-20 17:16:02.000000000","message":"I was actually thinking of the \u0027create_limit\u0027 API the user can call. Let\u0027s say they have a limit of 10 for \u0027instances\u0027 resource in the main DB. The way this is currently written, if they use create_limit to create a limit of 20 for \u0027instances\u0027, that will go in the API DB. Then they will have one \u0027instances\u0027 limit in the API DB and one in the main DB.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"b8b1073f5ec5e7c260e31a033f29a5bd21096c23","unresolved":false,"context_lines":[{"line_number":361,"context_line":"        try:"},{"line_number":362,"context_line":"            cls._destroy_all_in_db_by_project(context, project_id)"},{"line_number":363,"context_line":"        except exception.ProjectQuotaNotFound:"},{"line_number":364,"context_line":"            db.quota_destroy_all_by_project(context, project_id)"},{"line_number":365,"context_line":""},{"line_number":366,"context_line":"    @base.remotable_classmethod"},{"line_number":367,"context_line":"    def destroy_all_by_project_and_user(cls, context, project_id, user_id):"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_d27d811d","line":364,"updated":"2017-01-20 16:01:22.000000000","message":"If we could have duplicates, should we not actually try both?","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"0a4de0f8ba026a3b13d5d2cca82bb35789045093","unresolved":false,"context_lines":[{"line_number":361,"context_line":"        try:"},{"line_number":362,"context_line":"            cls._destroy_all_in_db_by_project(context, project_id)"},{"line_number":363,"context_line":"        except exception.ProjectQuotaNotFound:"},{"line_number":364,"context_line":"            db.quota_destroy_all_by_project(context, project_id)"},{"line_number":365,"context_line":""},{"line_number":366,"context_line":"    @base.remotable_classmethod"},{"line_number":367,"context_line":"    def destroy_all_by_project_and_user(cls, context, project_id, user_id):"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_c6869f12","line":364,"in_reply_to":"5a3905b3_4b19d887","updated":"2017-01-20 17:11:43.000000000","message":"... although now I worry about us deleting from the old DB, exactly when we are adding into the new DB, and then have a failure, eek. So we end up bringing something back from the dead on the retry.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"88465e698115fa5c984e5bfc9cba6342200b90e3","unresolved":false,"context_lines":[{"line_number":361,"context_line":"        try:"},{"line_number":362,"context_line":"            cls._destroy_all_in_db_by_project(context, project_id)"},{"line_number":363,"context_line":"        except exception.ProjectQuotaNotFound:"},{"line_number":364,"context_line":"            db.quota_destroy_all_by_project(context, project_id)"},{"line_number":365,"context_line":""},{"line_number":366,"context_line":"    @base.remotable_classmethod"},{"line_number":367,"context_line":"    def destroy_all_by_project_and_user(cls, context, project_id, user_id):"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_4b19d887","line":364,"in_reply_to":"5a3905b3_abd5dcb9","updated":"2017-01-20 17:10:42.000000000","message":"Yeah, been reading the migration more now.\n\nIf we get duplicates, the migration should really be in charge of removing any.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"4780c86c934608a3c871aede02a40598107af3ae","unresolved":false,"context_lines":[{"line_number":361,"context_line":"        try:"},{"line_number":362,"context_line":"            cls._destroy_all_in_db_by_project(context, project_id)"},{"line_number":363,"context_line":"        except exception.ProjectQuotaNotFound:"},{"line_number":364,"context_line":"            db.quota_destroy_all_by_project(context, project_id)"},{"line_number":365,"context_line":""},{"line_number":366,"context_line":"    @base.remotable_classmethod"},{"line_number":367,"context_line":"    def destroy_all_by_project_and_user(cls, context, project_id, user_id):"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_abd5dcb9","line":364,"in_reply_to":"5a3905b3_d27d811d","updated":"2017-01-20 16:58:57.000000000","message":"See my earlier comment, I think the points you\u0027re making illustrate why we should make sure duplicates can\u0027t be created.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"e3ae2c170d477857d76628f9e6a36f1794e21678","unresolved":false,"context_lines":[{"line_number":402,"context_line":""},{"line_number":403,"context_line":"    @base.remotable_classmethod"},{"line_number":404,"context_line":"    def create_class(cls, context, class_name, resource, limit):"},{"line_number":405,"context_line":"        cls._create_class_in_db(context, class_name, resource, limit)"},{"line_number":406,"context_line":""},{"line_number":407,"context_line":"    @base.remotable_classmethod"},{"line_number":408,"context_line":"    def update_class(cls, context, class_name, resource, limit):"}],"source_content_type":"text/x-python","patch_set":7,"id":"5a3905b3_cd4d65c5","line":405,"updated":"2017-01-24 09:50:44.000000000","message":"So, I think here we should check if this already exists in the old DB, and raise the usual exception if thats the case, to stop the duplicates.","commit_id":"14d36a7afc5221c6fe1f58fcd9b0cf1ebaf8b2e6"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"c1637bfe6f9d4e04e70ea4a408fda3a352b000f8","unresolved":false,"context_lines":[{"line_number":370,"context_line":"        try:"},{"line_number":371,"context_line":"            cls._destroy_all_in_db_by_project(context, project_id)"},{"line_number":372,"context_line":"        except exception.ProjectQuotaNotFound:"},{"line_number":373,"context_line":"            db.quota_destroy_all_by_project(context, project_id)"},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    @base.remotable_classmethod"},{"line_number":376,"context_line":"    def destroy_all_by_project_and_user(cls, context, project_id, user_id):"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a461143_643aa0cf","line":373,"updated":"2017-01-25 18:48:14.000000000","message":"This looks right on the surface, but I think (and I think John agrees) that maybe this should be just a graceful delete of both places. Is there any reason not to do that?","commit_id":"95be19c5ae4da5b1aaf4ae41c013a69597ce2c70"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"09e9bdc285f33d38028f0f25fe89e17d7ed9c8bc","unresolved":false,"context_lines":[{"line_number":370,"context_line":"        try:"},{"line_number":371,"context_line":"            cls._destroy_all_in_db_by_project(context, project_id)"},{"line_number":372,"context_line":"        except exception.ProjectQuotaNotFound:"},{"line_number":373,"context_line":"            db.quota_destroy_all_by_project(context, project_id)"},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    @base.remotable_classmethod"},{"line_number":376,"context_line":"    def destroy_all_by_project_and_user(cls, context, project_id, user_id):"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a461143_48ca12a3","line":373,"in_reply_to":"3a461143_4787660f","updated":"2017-01-25 21:10:31.000000000","message":"Yeah, pretty tiny race. Not sure we can do a lot better I guess, and limits coming back that have been deleted, which can be deleted again.. not the end of the world. Eventually consistent right?","commit_id":"95be19c5ae4da5b1aaf4ae41c013a69597ce2c70"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"cd72c225eeb39efc101fdd124df4f9581f8a6d22","unresolved":false,"context_lines":[{"line_number":370,"context_line":"        try:"},{"line_number":371,"context_line":"            cls._destroy_all_in_db_by_project(context, project_id)"},{"line_number":372,"context_line":"        except exception.ProjectQuotaNotFound:"},{"line_number":373,"context_line":"            db.quota_destroy_all_by_project(context, project_id)"},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    @base.remotable_classmethod"},{"line_number":376,"context_line":"    def destroy_all_by_project_and_user(cls, context, project_id, user_id):"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a461143_84e4ec0d","line":373,"in_reply_to":"3a461143_643aa0cf","updated":"2017-01-25 18:53:18.000000000","message":"This is the pattern I\u0027ve seen in InstanceGroup, Aggregate, and KeyPair, for example. I thought it was the preferred way because of that, I\u0027m not opinionated about it.","commit_id":"95be19c5ae4da5b1aaf4ae41c013a69597ce2c70"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"cdd47d91b9e73c9c4aec175368db9666912b3c5a","unresolved":false,"context_lines":[{"line_number":370,"context_line":"        try:"},{"line_number":371,"context_line":"            cls._destroy_all_in_db_by_project(context, project_id)"},{"line_number":372,"context_line":"        except exception.ProjectQuotaNotFound:"},{"line_number":373,"context_line":"            db.quota_destroy_all_by_project(context, project_id)"},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    @base.remotable_classmethod"},{"line_number":376,"context_line":"    def destroy_all_by_project_and_user(cls, context, project_id, user_id):"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a461143_4787660f","line":373,"in_reply_to":"3a461143_84e4ec0d","updated":"2017-01-25 19:11:32.000000000","message":"I am worried about the online migration script bringing things back to life.\n\nThinking about it, its an extreme edge case. We add the new one, but we delete it before we delete the one in the old DB, and the old DB deleting fails half way before getting to delete this one...\n\nI think thats the only case.","commit_id":"95be19c5ae4da5b1aaf4ae41c013a69597ce2c70"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"c1637bfe6f9d4e04e70ea4a408fda3a352b000f8","unresolved":false,"context_lines":[{"line_number":373,"context_line":"            db.quota_destroy_all_by_project(context, project_id)"},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    @base.remotable_classmethod"},{"line_number":376,"context_line":"    def destroy_all_by_project_and_user(cls, context, project_id, user_id):"},{"line_number":377,"context_line":"        try:"},{"line_number":378,"context_line":"            cls._destroy_all_in_db_by_project_and_user(context, project_id,"},{"line_number":379,"context_line":"                                                       user_id)"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a461143_a44ba858","line":376,"updated":"2017-01-25 18:48:14.000000000","message":"Here too?","commit_id":"95be19c5ae4da5b1aaf4ae41c013a69597ce2c70"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"b176c8e21990f3072d602d8b63639e95ca81a712","unresolved":false,"context_lines":[{"line_number":104,"context_line":"    @staticmethod"},{"line_number":105,"context_line":"    @db_api.api_context_manager.reader"},{"line_number":106,"context_line":"    def _get_all_from_db_by_project(context, project_id):"},{"line_number":107,"context_line":"        # by_project refers to the result that is a dict with a project_id key"},{"line_number":108,"context_line":"        rows \u003d context.session.query(api_models.Quota).\\"},{"line_number":109,"context_line":"                        filter_by(project_id\u003dproject_id).\\"},{"line_number":110,"context_line":"                        all()"}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_a412b260","line":107,"updated":"2017-03-07 20:07:43.000000000","message":"err, but it *isn\u0027t* returning a dict with a project_id key. This is returning a dict that looks like this:\n\n {\n   \u0027project_id\u0027: {project_uuid},\n   \u0027cpu\u0027: {limit_cpu},\n   \u0027ram\u0027: {limit_ram},\n   ...\n }\n\nI\u0027d actually recommend removing this (and the corresponding get_all_by_project() method below and doing this kind of transformation in the caller and sticking with a single get_all_by_filters() method that would return a list of Quota objects that can then be transformed into maps or sets as needed by the caller.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"226d5736253c3c70ab4d09dd1c70b2c9b55915e3","unresolved":false,"context_lines":[{"line_number":104,"context_line":"    @staticmethod"},{"line_number":105,"context_line":"    @db_api.api_context_manager.reader"},{"line_number":106,"context_line":"    def _get_all_from_db_by_project(context, project_id):"},{"line_number":107,"context_line":"        # by_project refers to the result that is a dict with a project_id key"},{"line_number":108,"context_line":"        rows \u003d context.session.query(api_models.Quota).\\"},{"line_number":109,"context_line":"                        filter_by(project_id\u003dproject_id).\\"},{"line_number":110,"context_line":"                        all()"}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_add500ba","line":107,"in_reply_to":"9a30ddce_a412b260","updated":"2017-03-08 23:02:54.000000000","message":"Yeah, maybe I should have put project_id in quotes \u0027project_id\u0027. It returns a dict that has a key that is \u0027project_id\u0027.\n\nI did this to keep things similar enough to what we currently have, to avoid disturbing the house of cards that is nova/quota.py.\n\nLet me think more about what it would take to do your suggestion.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"b176c8e21990f3072d602d8b63639e95ca81a712","unresolved":false,"context_lines":[{"line_number":137,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":138,"context_line":"        per_user \u003d context.session.query(api_models.ProjectUserQuota).\\"},{"line_number":139,"context_line":"                            filter_by(project_id\u003dproject_id).\\"},{"line_number":140,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":141,"context_line":"        if not per_project and not per_user:"},{"line_number":142,"context_line":"            raise exception.ProjectQuotaNotFound(project_id\u003dproject_id)"},{"line_number":143,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_aff36b09","line":140,"updated":"2017-03-07 20:07:43.000000000","message":"Couple things...\n\nI checked and there\u0027s no foreign key relationships established on either the quotas or project_quota_usages tables, so the above will work fine. However, it\u0027s best practice to first delete child records and then delete parent records, so I would delete the project_quota_usages records and *then* delete the quotas records.\n\nAlso, you will want to do the deletion within a transaction, otherwise there is a danger you will delete the project usage records and not the quota records. So, do this:\n\n def _destroy_all_in_db_by_project(context, project_id):\n   with context.session.begin():\n       per_user \u003d ...\n       per_project \u003d ...","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"381e71bb427e39dbce57460ad2875e3f6d4b94de","unresolved":false,"context_lines":[{"line_number":137,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":138,"context_line":"        per_user \u003d context.session.query(api_models.ProjectUserQuota).\\"},{"line_number":139,"context_line":"                            filter_by(project_id\u003dproject_id).\\"},{"line_number":140,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":141,"context_line":"        if not per_project and not per_user:"},{"line_number":142,"context_line":"            raise exception.ProjectQuotaNotFound(project_id\u003dproject_id)"},{"line_number":143,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_fe695c26","line":140,"in_reply_to":"9a30ddce_3eef74b1","updated":"2017-03-09 00:43:24.000000000","message":"Gah, sorry, typed the wrong thing.\n\n*these are not parent/child record relationships","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"c1efa0f1338f35e9458f8c1698a562fb98ae2ac8","unresolved":false,"context_lines":[{"line_number":137,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":138,"context_line":"        per_user \u003d context.session.query(api_models.ProjectUserQuota).\\"},{"line_number":139,"context_line":"                            filter_by(project_id\u003dproject_id).\\"},{"line_number":140,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":141,"context_line":"        if not per_project and not per_user:"},{"line_number":142,"context_line":"            raise exception.ProjectQuotaNotFound(project_id\u003dproject_id)"},{"line_number":143,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_06f9814d","line":140,"in_reply_to":"9a30ddce_59ea8210","updated":"2017-03-09 08:30:37.000000000","message":"Okay, just tried this and had my functional test fail with \"sqlalchemy.exc.InvalidRequestError: A transaction is already begun\" [1]. I think the @db_api.api_context_manager.writer decorator keeps things in a transaction itself, no?\n\n[1] http://paste.openstack.org/show/602046/","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"2a3c0b28116e621a1474b653b6a3994df3f7ed84","unresolved":false,"context_lines":[{"line_number":137,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":138,"context_line":"        per_user \u003d context.session.query(api_models.ProjectUserQuota).\\"},{"line_number":139,"context_line":"                            filter_by(project_id\u003dproject_id).\\"},{"line_number":140,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":141,"context_line":"        if not per_project and not per_user:"},{"line_number":142,"context_line":"            raise exception.ProjectQuotaNotFound(project_id\u003dproject_id)"},{"line_number":143,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_3eef74b1","line":140,"in_reply_to":"9a30ddce_6d99481f","updated":"2017-03-09 00:39:41.000000000","message":"Now that I started making the change, I notice these are parent/child record relationships. There are two separate tables for quotas, one that is for \"project-scoped\" quotas (networks, fixed_ips, floating_ips) and one that is for \"user-scoped\" quotas (instances, cores, ram, server_groups, ...). So they\u0027re lateral tables.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"226d5736253c3c70ab4d09dd1c70b2c9b55915e3","unresolved":false,"context_lines":[{"line_number":137,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":138,"context_line":"        per_user \u003d context.session.query(api_models.ProjectUserQuota).\\"},{"line_number":139,"context_line":"                            filter_by(project_id\u003dproject_id).\\"},{"line_number":140,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":141,"context_line":"        if not per_project and not per_user:"},{"line_number":142,"context_line":"            raise exception.ProjectQuotaNotFound(project_id\u003dproject_id)"},{"line_number":143,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_6d99481f","line":140,"in_reply_to":"9a30ddce_aff36b09","updated":"2017-03-08 23:02:54.000000000","message":"Thanks, will change this.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"a72c1e6a5b160efdbbeb5a3b2f22d1c1d934082d","unresolved":false,"context_lines":[{"line_number":137,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":138,"context_line":"        per_user \u003d context.session.query(api_models.ProjectUserQuota).\\"},{"line_number":139,"context_line":"                            filter_by(project_id\u003dproject_id).\\"},{"line_number":140,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":141,"context_line":"        if not per_project and not per_user:"},{"line_number":142,"context_line":"            raise exception.ProjectQuotaNotFound(project_id\u003dproject_id)"},{"line_number":143,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_59ea8210","line":140,"in_reply_to":"9a30ddce_fe695c26","updated":"2017-03-09 01:03:39.000000000","message":"Gotcha. Still should do the transactional safety, but I understand you on the lack of parent-child relations.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"b176c8e21990f3072d602d8b63639e95ca81a712","unresolved":false,"context_lines":[{"line_number":139,"context_line":"                            filter_by(project_id\u003dproject_id).\\"},{"line_number":140,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":141,"context_line":"        if not per_project and not per_user:"},{"line_number":142,"context_line":"            raise exception.ProjectQuotaNotFound(project_id\u003dproject_id)"},{"line_number":143,"context_line":""},{"line_number":144,"context_line":"    @staticmethod"},{"line_number":145,"context_line":"    @db_api.api_context_manager.writer"}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_4fe58f35","line":142,"updated":"2017-03-07 20:07:43.000000000","message":"Not sure it\u0027s all that useful to raise a NotFound actually... if the user intends to delete the records, who cares if they\u0027re not found (or already deleted by another thread). Result is the same.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"226d5736253c3c70ab4d09dd1c70b2c9b55915e3","unresolved":false,"context_lines":[{"line_number":139,"context_line":"                            filter_by(project_id\u003dproject_id).\\"},{"line_number":140,"context_line":"                            delete(synchronize_session\u003dFalse)"},{"line_number":141,"context_line":"        if not per_project and not per_user:"},{"line_number":142,"context_line":"            raise exception.ProjectQuotaNotFound(project_id\u003dproject_id)"},{"line_number":143,"context_line":""},{"line_number":144,"context_line":"    @staticmethod"},{"line_number":145,"context_line":"    @db_api.api_context_manager.writer"}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_ed7378e5","line":142,"in_reply_to":"9a30ddce_4fe58f35","updated":"2017-03-08 23:02:54.000000000","message":"I think I did this for the api db/main db straddle so I would know to try deleting them in the main db if they weren\u0027t found (to delete) in the api db. See L369 and L376.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"b176c8e21990f3072d602d8b63639e95ca81a712","unresolved":false,"context_lines":[{"line_number":150,"context_line":"                        delete(synchronize_session\u003dFalse)"},{"line_number":151,"context_line":"        if not result:"},{"line_number":152,"context_line":"            raise exception.ProjectUserQuotaNotFound(project_id\u003dproject_id,"},{"line_number":153,"context_line":"                                                     user_id\u003duser_id)"},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"    @staticmethod"},{"line_number":156,"context_line":"    @db_api.api_context_manager.reader"}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_afca8b9f","line":153,"updated":"2017-03-07 20:07:43.000000000","message":"ditto about not raising a NotFound","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"226d5736253c3c70ab4d09dd1c70b2c9b55915e3","unresolved":false,"context_lines":[{"line_number":150,"context_line":"                        delete(synchronize_session\u003dFalse)"},{"line_number":151,"context_line":"        if not result:"},{"line_number":152,"context_line":"            raise exception.ProjectUserQuotaNotFound(project_id\u003dproject_id,"},{"line_number":153,"context_line":"                                                     user_id\u003duser_id)"},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"    @staticmethod"},{"line_number":156,"context_line":"    @db_api.api_context_manager.reader"}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_8d459c71","line":153,"in_reply_to":"9a30ddce_afca8b9f","updated":"2017-03-08 23:02:54.000000000","message":"Same comment, see L376.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"b176c8e21990f3072d602d8b63639e95ca81a712","unresolved":false,"context_lines":[{"line_number":313,"context_line":"    @base.remotable_classmethod"},{"line_number":314,"context_line":"    def create_limit(cls, context, project_id, resource, limit, user_id\u003dNone):"},{"line_number":315,"context_line":"        try:"},{"line_number":316,"context_line":"            db.quota_get(context, project_id, resource, user_id\u003duser_id)"},{"line_number":317,"context_line":"        except exception.QuotaNotFound:"},{"line_number":318,"context_line":"            cls._create_limit_in_db(context, project_id, resource, limit,"},{"line_number":319,"context_line":"                                    user_id\u003duser_id)"}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_4f9d8f64","line":316,"updated":"2017-03-07 20:07:43.000000000","message":"Shouldn\u0027t the above be:\n\n self.get(context, project_id, resource, user_id)\n\n?\n\notherwise the create_limit() will only ever be checking for the existence of quotas in the *old* DB and never the new API DB.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"e43978f842e681b7d34789441db8768259d8b585","unresolved":false,"context_lines":[{"line_number":313,"context_line":"    @base.remotable_classmethod"},{"line_number":314,"context_line":"    def create_limit(cls, context, project_id, resource, limit, user_id\u003dNone):"},{"line_number":315,"context_line":"        try:"},{"line_number":316,"context_line":"            db.quota_get(context, project_id, resource, user_id\u003duser_id)"},{"line_number":317,"context_line":"        except exception.QuotaNotFound:"},{"line_number":318,"context_line":"            cls._create_limit_in_db(context, project_id, resource, limit,"},{"line_number":319,"context_line":"                                    user_id\u003duser_id)"}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_0f2397be","line":316,"in_reply_to":"9a30ddce_4f9d8f64","updated":"2017-03-07 20:09:08.000000000","message":"After reading back through the comments and the patch commit message, I understand that the migration of quotas from cell to API DB happens in the next patches, and this patch only wants to create in the API DB. That\u0027s cool by me.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"eae0922f9e150551a01d26b6ee029776a80f80d3","unresolved":false,"context_lines":[{"line_number":59,"context_line":"    #              get_all_by_project_and_user(), destroy_all_by_project(),"},{"line_number":60,"context_line":"    #              destroy_all_by_project_and_user(), get_class(),"},{"line_number":61,"context_line":"    #              get_default_class(), get_all_class_by_name(),"},{"line_number":62,"context_line":"    #              create_class(), update_class()"},{"line_number":63,"context_line":"    VERSION \u003d \u00271.3\u0027"},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"    fields \u003d {"}],"source_content_type":"text/x-python","patch_set":16,"id":"1a1ced50_32e7f7e5","line":62,"updated":"2017-03-17 14:34:20.000000000","message":"I would be tempted to add the methods, then move to doing the API preference in a separate patch, maybe.","commit_id":"2c77c637db83ff8cea0bdc97042bf672d8eae2cb"},{"author":{"_account_id":782,"name":"John Garbutt","email":"john@johngarbutt.com","username":"johngarbutt"},"change_message_id":"eae0922f9e150551a01d26b6ee029776a80f80d3","unresolved":false,"context_lines":[{"line_number":365,"context_line":"            main_db_quotas_dict[k] \u003d v"},{"line_number":366,"context_line":"        return main_db_quotas_dict"},{"line_number":367,"context_line":""},{"line_number":368,"context_line":"    @base.remotable_classmethod"},{"line_number":369,"context_line":"    def destroy_all_by_project(cls, context, project_id):"},{"line_number":370,"context_line":"        try:"},{"line_number":371,"context_line":"            cls._destroy_all_in_db_by_project(context, project_id)"},{"line_number":372,"context_line":"        except exception.ProjectQuotaNotFound:"},{"line_number":373,"context_line":"            db.quota_destroy_all_by_project(context, project_id)"},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    @base.remotable_classmethod"},{"line_number":376,"context_line":"    def destroy_all_by_project_and_user(cls, context, project_id, user_id):"}],"source_content_type":"text/x-python","patch_set":16,"id":"1a1ced50_813bf2ea","line":373,"range":{"start_line":368,"start_character":0,"end_line":373,"end_character":64},"updated":"2017-03-17 14:34:20.000000000","message":"Did we get comfy that this works out OK now. I remember going through all that in my head, and getting a bit lost a few times.\n\nIts the delete something and the sync logic brings it back to life problem.\n\nWe are probably OK because update happens in place, where ever it happens to be. So we should be OK?","commit_id":"2c77c637db83ff8cea0bdc97042bf672d8eae2cb"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"cd06798fab6f2be7c779a0e8d21de3aa38af9571","unresolved":false,"context_lines":[{"line_number":338,"context_line":"        except exception.QuotaNotFound:"},{"line_number":339,"context_line":"            quota \u003d db.quota_get(context, project_id, resource,"},{"line_number":340,"context_line":"                                 user_id\u003duser_id)"},{"line_number":341,"context_line":"        return quota"},{"line_number":342,"context_line":""},{"line_number":343,"context_line":"    @base.remotable_classmethod"},{"line_number":344,"context_line":"    def get_all(cls, context, project_id):"}],"source_content_type":"text/x-python","patch_set":17,"id":"7ffa3b31_64c3d339","line":341,"range":{"start_line":341,"start_character":15,"end_line":341,"end_character":20},"updated":"2017-04-19 21:19:51.000000000","message":"Isn\u0027t this going to return a dict and not a Quota object?\n\nSame for all the below actually.","commit_id":"e56bfd81f941dd3f46a34ca07102aefcf23b20c7"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"4691e49e8ff04bcda521c2e06f14ea0ee97cbcf3","unresolved":false,"context_lines":[{"line_number":338,"context_line":"        except exception.QuotaNotFound:"},{"line_number":339,"context_line":"            quota \u003d db.quota_get(context, project_id, resource,"},{"line_number":340,"context_line":"                                 user_id\u003duser_id)"},{"line_number":341,"context_line":"        return quota"},{"line_number":342,"context_line":""},{"line_number":343,"context_line":"    @base.remotable_classmethod"},{"line_number":344,"context_line":"    def get_all(cls, context, project_id):"}],"source_content_type":"text/x-python","patch_set":17,"id":"7ffa3b31_441c57c0","line":341,"range":{"start_line":341,"start_character":15,"end_line":341,"end_character":20},"in_reply_to":"7ffa3b31_64c3d339","updated":"2017-04-19 21:29:36.000000000","message":"Yes, this is something that\u0027s weird about the Quotas object is that it hasn\u0027t been a representation of any database model object. It\u0027s been a passthrough to the global QuotaEngine. These methods aren\u0027t doing that but they are passing through to access the database and return database model objects.\n\nTo have something like a real \"Quota object\" we would have to think differently about this and I\u0027m not sure whether we would try to add more fields to this Quotas object or add other quota-related objects?","commit_id":"e56bfd81f941dd3f46a34ca07102aefcf23b20c7"}],"nova/tests/functional/db/test_quotas.py":[{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"b176c8e21990f3072d602d8b63639e95ca81a712","unresolved":false,"context_lines":[{"line_number":205,"context_line":"                self.context, \u0027foo\u0027)"},{"line_number":206,"context_line":"        self.assertEqual(\u0027foo\u0027, limits_dict[\u0027class_name\u0027])"},{"line_number":207,"context_line":"        self.assertEqual(5, limits_dict[\u0027instances\u0027])"},{"line_number":208,"context_line":"        self.assertEqual(10, limits_dict[\u0027cores\u0027])"}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_2f785bf2","line":208,"updated":"2017-03-07 20:07:43.000000000","message":"I think we need a functional test that stresses the scenario of having quotas in both the old cell databases AND the new API database.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"226d5736253c3c70ab4d09dd1c70b2c9b55915e3","unresolved":false,"context_lines":[{"line_number":205,"context_line":"                self.context, \u0027foo\u0027)"},{"line_number":206,"context_line":"        self.assertEqual(\u0027foo\u0027, limits_dict[\u0027class_name\u0027])"},{"line_number":207,"context_line":"        self.assertEqual(5, limits_dict[\u0027instances\u0027])"},{"line_number":208,"context_line":"        self.assertEqual(10, limits_dict[\u0027cores\u0027])"}],"source_content_type":"text/x-python","patch_set":11,"id":"9a30ddce_c824e2b0","line":208,"in_reply_to":"9a30ddce_2f785bf2","updated":"2017-03-08 23:02:54.000000000","message":"What kind of test did you have in mind? I covered the quotas in api and main dbs in the unit tests in this patch and I\u0027m not sure what case I\u0027m missing.\n\nAs you noted earlier, the data migration part is in the next patch and that has migration functional tests.","commit_id":"1518e4462ef09ac6313f8eb5e9dc2c9a1da6420c"}],"nova/tests/unit/objects/test_objects.py":[{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"a7481f98d098156d4f496c57bb06595e2ef60bea","unresolved":false,"context_lines":[{"line_number":1142,"context_line":"    \u0027PciDevicePoolList\u0027: \u00271.1-15ecf022a68ddbb8c2a6739cfc9f8f5e\u0027,"},{"line_number":1143,"context_line":"    \u0027PowerVMLiveMigrateData\u0027: \u00271.1-ac0fdd26da685f12d7038782cabd393a\u0027,"},{"line_number":1144,"context_line":"    \u0027Quotas\u0027: \u00271.2-4f440b711a8b76b24601494fbf76a9d6\u0027,"},{"line_number":1145,"context_line":"    \u0027QuotasNoOp\u0027: \u00271.2-b3ac89871faa99ffc32cd5500236b9fd\u0027,"},{"line_number":1146,"context_line":"    \u0027RequestSpec\u0027: \u00271.8-35033ecef47a880f9a5e46e2269e2b97\u0027,"},{"line_number":1147,"context_line":"    \u0027ResourceClass\u0027: \u00271.0-e6b367e2cf1733c5f3526f20a3286fe9\u0027,"},{"line_number":1148,"context_line":"    \u0027ResourceClassList\u0027: \u00271.1-15ecf022a68ddbb8c2a6739cfc9f8f5e\u0027,"}],"source_content_type":"text/x-python","patch_set":6,"id":"9a57fde8_703edcb9","line":1145,"updated":"2017-01-12 14:59:07.000000000","message":"You added remotable methods, so you need a version bump, which is what this hash change is telling you :)","commit_id":"6c02312450db9b2fb280146889c551235028c589"}]}
