def configure(count, name, policy): """Configure application monitor""" restapi = context.GLOBAL.cell_api() url = _REST_PATH + name options = {} if count is not None: options['count'] = count if policy is not None: options['policy'] = policy # reconfigure if any of the parameters is specified if options: existing = None try: existing = restclient.get(restapi, url).json() except restclient.NotFoundError: _LOGGER.debug('App monitor not found: %s', name) if existing is None: _check_configure_usage(count) _LOGGER.debug('Creating app monitor: %s', name) restclient.post(restapi, url, payload=options) else: existing.update(options) _LOGGER.debug('Updating app monitor: %s', name) restclient.put(restapi, url, payload=existing) _LOGGER.debug('Retrieving app monitor: %s', name) monitor_entry = restclient.get(restapi, url) cli.out(formatter(monitor_entry.json()))
def configure(job_id, event, resource, expression, count): """Create or modify an existing app start schedule""" restapi = context.GLOBAL.cell_api(ctx['api']) url = _REST_PATH + job_id data = {} if event: data['event'] = event if resource: data['resource'] = resource if expression: data['expression'] = expression if count is not None: data['count'] = count if data: try: _LOGGER.debug('Creating cron job: %s', job_id) restclient.post(restapi, url, payload=data) except restclient.AlreadyExistsError: _LOGGER.debug('Updating cron job: %s', job_id) restclient.put(restapi, url, payload=data) _LOGGER.debug('Retrieving cron job: %s', job_id) job = restclient.get(restapi, url).json() _LOGGER.debug('job: %r', job) cli.out(_FORMATTER(job))
def configure(tenant, systems, name, env): """Configure allocation tenant.""" restapi = context.GLOBAL.admin_api(ctx.get('api')) tenant_url = '/tenant/%s' % tenant if systems: try: existing = restclient.get(restapi, tenant_url).json() all_systems = set(existing['systems']) all_systems.update(map(int, systems)) restclient.put(restapi, tenant_url, payload={'systems': list(all_systems)}) except restclient.NotFoundError: restclient.post(restapi, tenant_url, payload={'systems': map(int, systems)}) if env: if name is None: name = env alloc_url = '/allocation/%s/%s' % (tenant, name) try: restclient.post(restapi, alloc_url, payload={'environment': env}) except restclient.AlreadyExistsError: pass _display_tenant(restapi, tenant)
def configure(name, cell, pattern, endpoints, alias, scope): """Create, modify or get Treadmill App DNS entry""" restapi = context.GLOBAL.admin_api() url = _REST_PATH + name data = {} if cell: data['cells'] = cell if pattern is not None: data['pattern'] = pattern if endpoints is not None: data['endpoints'] = endpoints if alias is not None: data['alias'] = alias if scope is not None: data['scope'] = scope if data: try: _LOGGER.debug('Trying to create app-dns entry %s', name) restclient.post(restapi, url, data) except restclient.AlreadyExistsError: _LOGGER.debug('Updating app-dns entry %s', name) restclient.put(restapi, url, data) _LOGGER.debug('Retrieving App DNS entry %s', name) app_dns_entry = restclient.get(restapi, url).json() cli.out(formatter(app_dns_entry))
def _make_allocation(restapi, allocation, env): """Ensure allocation exists for given environment.""" # Make sure allocation exists for given environment. alloc_url = '/allocation/{}/{}'.format(allocation, env) try: restclient.post(restapi, alloc_url, payload={'environment': env}) except restclient.AlreadyExistsError: pass
def test_default_timeout_post(self, resp_mock): """Tests that default timeout for post request is set correctly.""" resp_mock.return_value.status_code = http.client.OK resp_mock.return_value.text = 'foo' restclient.post('http://foo.com', '/', '') resp_mock.assert_called_with( 'http://foo.com/', stream=None, auth=mock.ANY, headers=None, json='', timeout=(0.5, None), proxies=None )
def reserve(allocation, env, cell, partition, rank, rank_adjustment, max_utilization, empty, memory, cpu, disk, traits): """Reserve capacity on the cell for given environment.""" _check_reserve_usage(empty, memory, cpu, disk) restapi = context.GLOBAL.admin_api(ctx.get('api')) _check_tenant_exists(restapi, allocation) _make_allocation(restapi, allocation, env) data = {} if empty: data['memory'] = '0M' data['disk'] = '0M' data['cpu'] = '0%' if memory: data['memory'] = memory if cpu: data['cpu'] = cpu if disk: data['disk'] = disk if rank is not None: data['rank'] = rank if rank_adjustment is not None: data['rank_adjustment'] = rank_adjustment if max_utilization is not None: data['max_utilization'] = max_utilization if partition: data['partition'] = partition if traits: data['traits'] = cli.combine(traits) if data: reservation_url = '/allocation/{}/{}/reservation/{}'.format( allocation, env, cell) try: existing = restclient.get(restapi, reservation_url).json() # TODO: need cleaner way of deleting attributes that are not # valid for update. It is a hack. for attr in list(existing): if (attr not in ['memory', 'cpu', 'disk', 'partition']): del existing[attr] existing.update(data) restclient.put(restapi, reservation_url, payload=existing) except restclient.NotFoundError: # some attributes need default values when creating if not partition: data['partition'] = admin.DEFAULT_PARTITION restclient.post(restapi, reservation_url, payload=data) _display_tenant(restapi, allocation)
def reserve(allocation, env, cell, partition, rank, rank_adjustment, max_utilization, empty, memory, cpu, disk, traits): """Reserve capacity on the cell for given environment.""" _check_reserve_usage(empty, memory, cpu, disk) restapi = context.GLOBAL.admin_api(ctx.get('api')) _check_tenant_exists(restapi, allocation) _make_allocation(restapi, allocation, env) data = {} if empty: data['memory'] = '0M' data['disk'] = '0M' data['cpu'] = '0%' if memory: data['memory'] = memory if cpu: data['cpu'] = cpu if disk: data['disk'] = disk if partition: data['partition'] = partition reservation_url = '/allocation/{}/{}/reservation/{}'.format( allocation, env, cell) try: existing = restclient.get(restapi, reservation_url).json() if data: # To meet the json schema required in create if 'memory' not in data: data['memory'] = existing['memory'] if 'cpu' not in data: data['cpu'] = existing['cpu'] if 'disk' not in data: data['disk'] = existing['disk'] if 'partition' not in data: data['partition'] = existing['partition'] restclient.put(restapi, reservation_url, payload=data) except restclient.NotFoundError: if rank is not None: data['rank'] = rank if rank_adjustment is not None: data['rank_adjustment'] = rank_adjustment if max_utilization is not None: data['max_utilization'] = max_utilization if traits: data['traits'] = cli.combine(traits) restclient.post(restapi, reservation_url, payload=data) _display_tenant(restapi, allocation)
def assign(allocation, env, cell, pattern, priority, delete): """Assign application pattern:priority to the allocation. Application pattern must start with <PROID>. and is a glob expression. Environments of the proid and one specified in command line using --env option must match. Once scheduled, Treadmill scheduler will match application against all available patterns and assign application to a reserved capacity. All application assigned to a capacity are ordered by priority from high to low. """ restapi = context.GLOBAL.admin_api(ctx.get('api')) _check_tenant_exists(restapi, allocation) _make_allocation(restapi, allocation, env) reservation_url = '/allocation/{}/{}/reservation/{}'.format( allocation, env, cell) try: restclient.get(restapi, reservation_url) except restclient.NotFoundError: # TODO: default partition should be resolved in API, not in CLI. restclient.post(restapi, reservation_url, payload={ 'memory': '0M', 'disk': '0M', 'cpu': '0%', 'partition': admin.DEFAULT_PARTITION }) url = '/allocation/{}/{}/assignment/{}/{}'.format( allocation, env, cell, pattern) if delete: restclient.delete(restapi, url) else: default_prio = None existing = restclient.get(restapi, url).json() for assignment in existing: if assignment['pattern'] == pattern: default_prio = assignment['priority'] if default_prio is None: default_prio = _DEFAULT_PRIORITY data = {'priority': priority if priority else default_prio} restclient.put(restapi, url, payload=data) _display_tenant(restapi, allocation)
def test_verify_post(self, resp_mock): """Tests that 'verify' for post request is set correctly.""" resp_mock.return_value.status_code = http_client.OK resp_mock.return_value.text = 'foo' restclient.post('http://foo.com', '/', '', verify='/path/to/ca/certs') resp_mock.assert_called_with('http://foo.com/', stream=None, auth=mock.ANY, headers=None, json='', timeout=(0.5, None), proxies=None, verify='/path/to/ca/certs')
def _configure_monitor(name, count): """Configure target count for the current cell.""" _LOGGER.info('configuring monitor: %s, count: %s', name, count) url = '/app-monitor/%s' % name restapi = context.GLOBAL.cell_api() data = {'count': count} try: _LOGGER.debug('Creating app monitor: %s', name) restclient.post(restapi, url, payload=data) except restclient.AlreadyExistsError: _LOGGER.debug('Updating app monitor: %s', name) restclient.put(restapi, url, payload=data)
def assign(allocation, cell, pattern, priority, delete): """Assign application pattern:priority to the allocation.""" restapi = context.GLOBAL.admin_api(ctx.get('api')) url = '/allocation/%s/assignment/%s/%s' % (allocation, cell, pattern) if delete: restclient.delete(restapi, url) else: try: restclient.post(restapi, url, payload={'priority': priority}) except restclient.AlreadyExistsError: restclient.put(restapi, url, payload={'priority': priority}) _display_alloc(restapi, allocation)
def assign(allocation, env, cell, pattern, priority, delete): """Assign application pattern:priority to the allocation. Application pattern must start with <PROID>. and is a glob expression. Environments of the proid and one specified in command line using --env option must match. Once scheduled, Treadmill scheduler will match application against all available patterns and assign application to a reserved capacity. All application assigned to a capacity are ordered by priority from high to low. """ restapi = context.GLOBAL.admin_api() _check_tenant_exists(restapi, allocation) _make_allocation(restapi, allocation, env) reservation_url = '/allocation/{}/{}/reservation/{}'.format( allocation, env, cell) try: restclient.get(restapi, reservation_url) except restclient.NotFoundError: restclient.post(restapi, reservation_url, payload={ 'memory': '0M', 'disk': '0M', 'cpu': '0%' }) url = '/allocation/{}/{}/assignment/{}/{}'.format( allocation, env, cell, pattern) if delete: restclient.delete(restapi, url) else: data = {} if priority: data['priority'] = priority existing = restclient.get(restapi, url).json() rest_func = restclient.post for assignment in existing: if assignment['pattern'] == pattern: rest_func = restclient.put break rest_func(restapi, url, payload=data) _display_tenant(restapi, allocation)
def set_count(monitor_name=None, count=None): """Set the count on the supplied monitor""" _LOGGER.debug('monitor: %s, count: %s', monitor_name, count) if not monitor_name: _LOGGER.error('No monitor name supplied, cannot continue') return restclient.post( [_API_URL], '/app-monitor/{}'.format(monitor_name), payload={'count': count}, headers={'X-Treadmill-Trusted-Agent': 'cron'} )
def reserve(allocation, cell, partition, rank, memory, cpu, disk): """Reserve capacity on the cell.""" restapi = context.GLOBAL.admin_api(ctx.get('api')) if (cell is None and any([memory, cpu, disk, rank is not None, partition])): raise click.UsageError( 'Must specify cell with modifying reservation.') if cell: # Create reservation for the given cell. reservation_url = '/allocation/%s/reservation/%s' % (allocation, cell) try: existing = restclient.get(restapi, reservation_url).json() # TODO: need cleaner way of deleting attributes that are not # valid for update. It is a hack. for attr in existing.keys(): if (attr not in [ 'memory', 'cpu', 'disk', 'rank', 'partition' ]): del existing[attr] if memory: existing['memory'] = memory if cpu: existing['cpu'] = cpu if disk: existing['disk'] = disk if rank is not None: existing['rank'] = rank if partition: existing['partition'] = partition restclient.put(restapi, reservation_url, payload=existing) except restclient.NotFoundError: payload = { 'memory': memory, 'disk': disk, 'cpu': cpu, 'rank': rank if rank is not None else 100 } if partition: payload['partition'] = partition else: payload['partition'] = admin.DEFAULT_PARTITION restclient.post(restapi, reservation_url, payload=payload) _display_alloc(restapi, allocation)
def configure_node(ctx, vpc_name, region, name, key, image, subnet_name, instance_type, tm_release, app_root, ipa_admin_password, with_api, manifest): """Configure new Node in Cell""" domain = ctx.obj['DOMAIN'] _url = '/cloud/vpc/' + vpc_name + '/domain/' + domain \ + '/server/' + name cli.out( restclient.post( api=ctx.obj.get('API'), url=_url, payload={ "role": "node", "key": key, "tm_release": tm_release, "region": region, "app_root": app_root, "instance_type": instance_type, "image": image, "ipa_admin_password": ipa_admin_password, "subnet_name": subnet_name, "with_api": with_api, }, headers={'Content-Type': 'application/json'} ).content )
def configure_cell(ctx, vpc_name, region, name, key, image, subnet_name, instance_type, tm_release, app_root, cidr_block, ipa_admin_password, manifest): """Configure Treadmill Cell""" domain = ctx.obj['DOMAIN'] _url = '/cloud/vpc/' + vpc_name + '/domain/' + domain + '/cell' cli.out( restclient.post( api=ctx.obj.get('API'), url=_url, payload={ "role": "cell", "key": key, "tm_release": tm_release, "region": region, "app_root": app_root, "cidr_block": cidr_block, "instance_type": instance_type, "image": image, "ipa_admin_password": ipa_admin_password, "subnet_name": subnet_name }, headers={'Content-Type': 'application/json'} ).content )
def start(job_id=None, app_name=None, count=1): """Start an application in the given cell""" _LOGGER.debug('job_id: %s, app_name: %s, count: %s', job_id, app_name, count) if not app_name: _LOGGER.error('No app name provided, cannot continue') return try: restclient.post([_API_URL], '/instance/{}?count={}'.format(app_name, count), payload={}, headers={'X-Treadmill-Trusted-Agent': 'cron'}) except Exception: # pylint: disable=W0703 _LOGGER.exception('Error starting app: %s', app_name)
def post(path, payload): """REST POST request.""" request = yaml.load(payload.read()) response = restclient.post(ctx['api'], path, payload=request) formatter = cli.make_formatter(None) cli.out(formatter(response.json()))
def test_raw_payload_post(self, resp_mock): """Tests that post can send payload not in json.""" resp_mock.return_value.status_code = http_client.OK resp_mock.return_value.text = 'foo' restclient.post('http://foo.com', '/', 'payload', payload_to_json=False) resp_mock.assert_called_with('http://foo.com/', stream=None, auth=mock.ANY, headers=None, timeout=(0.5, None), proxies=None, data='payload', verify=True)
def authorize(self, resource, action, args, _kwargs): """Delegate authorization to the plugin.""" resource = resource.split('.').pop() user = self.user_clbk() # PGE API can't handle None. if user is None: user = '' # Defaults for primary key and payload. url = '/%s/%s/%s' % (user, action, resource) data = {} if len(args) > 0: data['pk'] = str(args[0]) if len(args) > 1: data['payload'] = args[1] # POST http://auth_server/user/action/resource # {"pk": "foo", "payload": { ... }} response = restclient.post(api=self.remote, url=url, payload=data, auth=None) authd = response.json() _LOGGER.debug('client authorize ressult %r', authd) if not authd['auth']: raise AuthorizationError(authd['annotations']) return authd
def authorize(self, resource, action, args, _kwargs): """Delegate authorization to the plugin.""" resource = resource.split('.').pop() user = self.user_clbk() # PGE API can't handle None. if user is None: user = '' # Defaults for primary key and payload. url = '/%s/%s/%s' % (user, action, resource) data = {} nargs = len(args) if nargs > 0: data['pk'] = args[0] if nargs > 1: data['payload'] = args[1] response = restclient.post( [self.remote], url, payload=data, ) authd = response.json() _LOGGER.debug('client authorize ressult %r', authd) if not authd['auth']: raise AuthorizationError(authd['annotations']) return authd
def _reap(bad): """Delete instances that did not path health check.""" if not bad: return [] cellapi = context.GLOBAL.cell_api() try: for instance in bad: _LOGGER.info('Delete: %s', instance) restclient.post(cellapi, '/instance/_bulk/delete', payload=dict(instances=bad)) return bad except Exception: # pylint: disable=W0703 _LOGGER.exception('Error reaping: %r', bad) return []
def configure(count, name): """Configure application monitor""" restapi = context.GLOBAL.cell_api() url = _REST_PATH + name if count is not None: data = {'count': count} try: _LOGGER.debug('Creating app monitor: %s', name) restclient.post(restapi, url, payload=data) except restclient.AlreadyExistsError: _LOGGER.debug('Updating app monitor: %s', name) restclient.put(restapi, url, payload=data) _LOGGER.debug('Retrieving app monitor: %s', name) monitor_entry = restclient.get(restapi, url) cli.out(formatter(monitor_entry.json()))
def post(path, payload): """REST POST request.""" with io.open(payload, 'rb') as fd: request = yaml.load(stream=fd) response = restclient.post(ctx['api'], path, payload=request) formatter = cli.make_formatter(None) cli.out(formatter(response.json()))
def create_user(ctx, username): """Creates an IPA User""" cli.out( restclient.post( api=ctx.obj.get('API'), url='/ipa/user/' + username, headers={'Content-Type': 'application/json'} ).content )
def create_host(ctx, hostname): """Creates an IPA Host""" cli.out( restclient.post( api=ctx.obj.get('API'), url='/ipa/host/' + hostname, headers={'Content-Type': 'application/json'} ).content )
def _run(apis, count, manifest, memory, cpu, disk, tickets, service, restart_limit, restart_interval, endpoint, appname, command): """Run Treadmill app.""" # too many branches # # pylint: disable=R0912 app = {} if manifest: app = yaml.load(manifest.read()) if endpoint: app['endpoints'] = [{ 'name': name, 'port': port } for name, port in endpoint] if tickets: app['tickets'] = tickets if command: if not service: # Take the basename of the command, always assume / on all # platforms. service = command[0].split('/')[-1] services_dict = {svc['name']: svc for svc in app.get('services', [])} if service: if service not in services_dict: services_dict[service] = { 'name': service, 'restart': { 'limit': restart_limit, 'interval': restart_interval, } } if command: services_dict[service]['command'] = ' '.join(list(command)) if services_dict: app['services'] = services_dict.values() if app: if memory: app['memory'] = memory if disk: app['disk'] = disk if cpu: app['cpu'] = cpu url = '/instance/' + appname if count: url += '?count=%d' % count response = restclient.post(apis, url, payload=app) for instance_id in response.json()['instances']: cli.out(instance_id)
def configure(allocation, systems, set_): """Configure allocation. Allocation name is global, and is associated with list of systems. """ restapi = context.GLOBAL.admin_api() url = '/tenant/{}'.format(allocation) if systems: # If tenant exists, update or replace it with new systems. # If update fails with resource does not exist error, try creating # tenants from parent to child, those that do not exist will be # created with provided systems. try: existing = restclient.get(restapi, url).json() all_systems = set(six.moves.map(int, systems)) # if the system ids have to be extended instead of replaced if not set_: all_systems.update(existing['systems']) restclient.put(restapi, url, payload={'systems': list(all_systems)}) except restclient.NotFoundError: # Create parent tenants recursively. # # If parent does not exist, it will be created with the systems # specified. parts = allocation.split(':') for idx in range(1, len(parts) + 1): url = '/tenant/{}'.format(':'.join(parts[:idx])) try: existing = restclient.get(restapi, url).json() except restclient.NotFoundError: restclient.post(restapi, url, payload={ 'systems': list(six.moves.map(int, systems)) }) _display_tenant(restapi, allocation)
def stop(job_id=None, app_name=None): """Stop an application""" _LOGGER.debug('job_id: %s, app_name: %r', job_id, app_name) try: response = restclient.get([_API_URL], '/instance/?match={}'.format(app_name)) instances = response.json().get('instances', []) _LOGGER.info('Stopping: %r', instances) if not instances: _LOGGER.warning('No instances running for %s', app_name) return restclient.post([_API_URL], '/instance/_bulk/delete', payload=dict(instances=instances), headers={'X-Treadmill-Trusted-Agent': 'cron'}) except Exception: # pylint: disable=W0703 _LOGGER.exception('Error stopping app: %s', app_name)