| # Copyright 2017 Google Inc. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """Schedule Info APIs implemented using Google Cloud Endpoints.""" |
| |
| import datetime |
| import endpoints |
| |
| from google.appengine.ext import ndb |
| |
| from webapp.src import vtslab_status as Status |
| from webapp.src.endpoint import endpoint_base |
| from webapp.src.proto import model |
| from webapp.src.utils import email_util |
| |
| SCHEDULE_INFO_RESOURCE = endpoints.ResourceContainer(model.ScheduleInfoMessage) |
| SCHEDULE_SUSPEND_RESOURCE = endpoints.ResourceContainer( |
| model.ScheduleSuspendMessage) |
| |
| |
| @endpoints.api(name="schedule", version="v1") |
| class ScheduleInfoApi(endpoint_base.EndpointBase): |
| """Endpoint API for schedule_info.""" |
| |
| @endpoints.method( |
| SCHEDULE_INFO_RESOURCE, |
| model.DefaultResponse, |
| path="clear", |
| http_method="POST", |
| name="clear") |
| def clear(self, request): |
| """Clears test schedule info in DB.""" |
| schedule_query = model.ScheduleModel.query( |
| model.ScheduleModel.schedule_type != "green") |
| existing_schedules = schedule_query.fetch(keys_only=True) |
| if existing_schedules and len(existing_schedules) > 0: |
| ndb.delete_multi(existing_schedules) |
| return model.DefaultResponse( |
| return_code=model.ReturnCodeMessage.SUCCESS) |
| |
| @endpoints.method( |
| SCHEDULE_INFO_RESOURCE, |
| model.DefaultResponse, |
| path="set", |
| http_method="POST", |
| name="set") |
| def set(self, request): |
| """Sets the schedule info based on `request`.""" |
| exist_on_both = self.GetCommonAttributes(request, model.ScheduleModel) |
| # check duplicates |
| exclusions = [ |
| "name", "schedule_type", "schedule", "param", "timestamp", |
| "children_jobs", "error_count", "suspended" |
| ] |
| # list of protorpc message fields. |
| duplicate_checklist = [x for x in exist_on_both if x not in exclusions] |
| empty_list_field = [] |
| query = model.ScheduleModel.query() |
| for attr_name in duplicate_checklist: |
| if model.ScheduleModel._properties[attr_name]._repeated: |
| value = request.get_assigned_value(attr_name) |
| if value: |
| query = query.filter( |
| getattr(model.ScheduleModel, attr_name).IN( |
| request.get_assigned_value(attr_name))) |
| else: |
| # empty list cannot be queried. |
| empty_list_field.append(attr_name) |
| else: |
| query = query.filter( |
| getattr(model.ScheduleModel, attr_name) == |
| request.get_assigned_value(attr_name)) |
| duplicated_schedules = query.fetch() |
| |
| if empty_list_field: |
| duplicated_schedules = [ |
| schedule for schedule in duplicated_schedules |
| if all( |
| [not getattr(schedule, attr) for attr in empty_list_field]) |
| ] |
| |
| if duplicated_schedules: |
| schedule = duplicated_schedules[0] |
| else: |
| schedule = model.ScheduleModel() |
| for attr_name in exist_on_both: |
| setattr(schedule, attr_name, |
| request.get_assigned_value(attr_name)) |
| schedule.schedule_type = "test" |
| schedule.error_count = 0 |
| schedule.suspended = False |
| schedule.priority_value = Status.GetPriorityValue(schedule.priority) |
| |
| schedule.timestamp = datetime.datetime.now() |
| schedule.put() |
| |
| return model.DefaultResponse( |
| return_code=model.ReturnCodeMessage.SUCCESS) |
| |
| @endpoints.method( |
| endpoint_base.GET_REQUEST_RESOURCE, |
| model.ScheduleResponseMessage, |
| path="get", |
| http_method="POST", |
| name="get") |
| def get(self, request): |
| """Gets the schedules from datastore.""" |
| return_list, more = self.Get(request=request, |
| metaclass=model.ScheduleModel, |
| message=model.ScheduleInfoMessage) |
| |
| return model.ScheduleResponseMessage( |
| schedules=return_list, has_next=more) |
| |
| @endpoints.method( |
| endpoint_base.COUNT_REQUEST_RESOURCE, |
| model.CountResponseMessage, |
| path="count", |
| http_method="POST", |
| name="count") |
| def count(self, request): |
| """Gets total number of ScheduleModel entities stored in datastore.""" |
| filters = self.CreateFilterList( |
| filter_string=request.filter, metaclass=model.ScheduleModel) |
| |
| count = self.Count(metaclass=model.ScheduleModel, filters=filters) |
| |
| return model.CountResponseMessage(count=count) |
| |
| @endpoints.method( |
| SCHEDULE_SUSPEND_RESOURCE, |
| model.ScheduleSuspendMessage, |
| path="suspend", |
| http_method="POST", |
| name="suspend") |
| def suspend(self, request): |
| """Toggles a schedule from suspend to resume, or vice versa.""" |
| schedules_to_put = [] |
| schedules_to_return = [] |
| for schedule in request.schedules: |
| schedule_key = ndb.key.Key(urlsafe=schedule.urlsafe_key) |
| schedule_entity = schedule_key.get() |
| if schedule.suspend: # to suspend |
| schedule_entity.suspended = True |
| else: # to resume |
| schedule_entity.error_count = 0 |
| schedule_entity.suspended = False |
| schedules_to_put.append(schedule_entity) |
| schedules_to_return.append({"urlsafe_key": schedule.urlsafe_key, |
| "suspend": schedule_entity.suspended}) |
| # TODO(jongmok): Minimize a number of emails by merging schedules. |
| email_util.send_schedule_suspension_notification(schedule_entity) |
| |
| ndb.put_multi(schedules_to_put) |
| return model.ScheduleSuspendMessage(schedules=schedules_to_return) |
| |
| |
| @endpoints.api(name="green_schedule_info", version="v1") |
| class GreenScheduleInfoApi(endpoint_base.EndpointBase): |
| """Endpoint API for green_schedule_info.""" |
| |
| @endpoints.method( |
| SCHEDULE_INFO_RESOURCE, |
| model.DefaultResponse, |
| path="clear", |
| http_method="POST", |
| name="clear") |
| def clear(self, request): |
| """Clears green build schedule info in DB.""" |
| schedule_query = model.ScheduleModel.query( |
| model.ScheduleModel.schedule_type == "green") |
| existing_schedules = schedule_query.fetch(keys_only=True) |
| if existing_schedules and len(existing_schedules) > 0: |
| ndb.delete_multi(existing_schedules) |
| return model.DefaultResponse( |
| return_code=model.ReturnCodeMessage.SUCCESS) |
| |
| @endpoints.method( |
| SCHEDULE_INFO_RESOURCE, |
| model.DefaultResponse, |
| path="set", |
| http_method="POST", |
| name="set") |
| def set(self, request): |
| """Sets the green build schedule info based on `request`.""" |
| schedule = model.ScheduleModel() |
| schedule.name = request.name |
| schedule.manifest_branch = request.manifest_branch |
| schedule.build_target = request.build_target |
| schedule.device_pab_account_id = request.device_pab_account_id |
| schedule.test_name = request.test_name |
| schedule.schedule = request.schedule |
| schedule.priority = request.priority |
| schedule.device = request.device |
| schedule.shards = request.shards |
| schedule.gsi_branch = request.gsi_branch |
| schedule.gsi_build_target = request.gsi_build_target |
| schedule.gsi_pab_account_id = request.gsi_pab_account_id |
| schedule.gsi_vendor_version = request.gsi_vendor_version |
| schedule.test_branch = request.test_branch |
| schedule.test_build_target = request.test_build_target |
| schedule.test_pab_account_id = request.test_pab_account_id |
| schedule.timestamp = datetime.datetime.now() |
| schedule.schedule_type = "green" |
| schedule.put() |
| |
| return model.DefaultResponse( |
| return_code=model.ReturnCodeMessage.SUCCESS) |