diff --git a/cmd/cascade_service.py b/cmd/cascade_service.py index 1160ce9..f6f7660 100644 --- a/cmd/cascade_service.py +++ b/cmd/cascade_service.py @@ -20,21 +20,66 @@ if __name__ == "__main__": eventlet.monkey_patch() import sys +import traceback from oslo_log import log as logging from oslo_config import cfg +from nova import exception as nova_exception +from nova import quota +import nova.db.api +from nova.conductor import rpcapi as conductor_rpcapi +from nova.i18n import _LE +import nova.objects as nova_objects +from nova.objects import base as objects_base +import nova.rpc as nova_rpc + import tricircle.cascade_service.service as service +def block_db_access(): + class NoDB(object): + def __getattr__(self, attr): + return self + + def __call__(self, *args, **kwargs): + stacktrace = "".join(traceback.format_stack()) + LOG = logging.getLogger('nova.compute') + LOG.error(_LE('No db access allowed in nova-compute: %s'), + stacktrace) + raise nova_exception.DBNotAllowed('nova-compute') + + nova.db.api.IMPL = NoDB() + + +def set_up_nova_object_indirection(): + conductor = conductor_rpcapi.ConductorAPI() + conductor.client.target.exchange = "nova" + objects_base.NovaObject.indirection_api = conductor + + def process_command_line_arguments(): - conf = cfg.ConfigOpts() - logging.register_options(conf) + logging.register_options(cfg.CONF) logging.set_defaults() - conf(sys.argv[1:]) - logging.setup(conf, "cascade_service", version='0.1') + cfg.CONF(sys.argv[1:]) + logging.setup(cfg.CONF, "cascade_service", version='0.1') + + +def _set_up_nova_objects(): + nova_rpc.init(cfg.CONF) + block_db_access() + set_up_nova_object_indirection() + nova_objects.register_all() + + +def _disable_quotas(): + QUOTAS = quota.QUOTAS + QUOTAS._driver_cls = quota.NoopQuotaDriver() + if __name__ == "__main__": + _set_up_nova_objects() + _disable_quotas() process_command_line_arguments() server = service.setup_server() server.start() diff --git a/devstack/local.conf.sample b/devstack/local.conf.sample index d4087f0..24f115d 100644 --- a/devstack/local.conf.sample +++ b/devstack/local.conf.sample @@ -37,6 +37,8 @@ enable_service t-svc-api # Use Neutron instead of nova-network disable_service n-net +disable_service n-cpu +disable_service n-sch enable_service q-svc disable_service q-dhcp disable_service q-l3 diff --git a/devstack/plugin.sh b/devstack/plugin.sh index ec3b04a..0582e97 100644 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -56,6 +56,9 @@ function configure_tricircle_plugin { sudo install -d -o $STACK_USER -m 755 $TRICIRCLE_CONF_DIR cp -p $TRICIRCLE_DIR/etc/cascade_service.conf $TRICIRCLE_CASCADE_CONF + TRICIRCLE_POLICY_FILE=$TRICIRCLE_CONF_DIR/policy.json + cp $TRICIRCLE_DIR/etc/policy.json $TRICIRCLE_POLICY_FILE + iniset $TRICIRCLE_CASCADE_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL iniset $TRICIRCLE_CASCADE_CONF DEFAULT verbose True setup_colorized_logging $TRICIRCLE_CASCADE_CONF DEFAULT @@ -117,7 +120,7 @@ if [[ "$Q_ENABLE_TRICIRCLE" == "True" ]]; then echo_summary "Initializing Cascading Service" if is_service_enabled t-svc; then - run_process t-svc "python $TRICIRCLE_CASCADE_SERVICE --config-file $TRICIRCLE_CASCADE_CONF" + run_process t-svc "python $TRICIRCLE_CASCADE_SERVICE --config-file $TRICIRCLE_CASCADE_CONF --config-dir $TRICIRCLE_CONF_DIR" fi if is_service_enabled t-svc-api; then diff --git a/etc/policy.json b/etc/policy.json new file mode 100644 index 0000000..568ce91 --- /dev/null +++ b/etc/policy.json @@ -0,0 +1,485 @@ +{ + "context_is_admin": "role:admin", + "admin_or_owner": "is_admin:True or project_id:%(project_id)s", + "default": "rule:admin_or_owner", + + "cells_scheduler_filter:TargetCellFilter": "is_admin:True", + + "compute:create": "", + "compute:create:attach_network": "", + "compute:create:attach_volume": "", + "compute:create:forced_host": "is_admin:True", + + "compute:get": "", + "compute:get_all": "", + "compute:get_all_tenants": "is_admin:True", + + "compute:update": "", + + "compute:get_instance_metadata": "", + "compute:get_all_instance_metadata": "", + "compute:get_all_instance_system_metadata": "", + "compute:update_instance_metadata": "", + "compute:delete_instance_metadata": "", + + "compute:get_instance_faults": "", + "compute:get_diagnostics": "", + "compute:get_instance_diagnostics": "", + + "compute:start": "rule:admin_or_owner", + "compute:stop": "rule:admin_or_owner", + + "compute:get_lock": "", + "compute:lock": "", + "compute:unlock": "", + "compute:unlock_override": "rule:admin_api", + + "compute:get_vnc_console": "", + "compute:get_spice_console": "", + "compute:get_rdp_console": "", + "compute:get_serial_console": "", + "compute:get_mks_console": "", + "compute:get_console_output": "", + + "compute:reset_network": "", + "compute:inject_network_info": "", + "compute:add_fixed_ip": "", + "compute:remove_fixed_ip": "", + + "compute:attach_volume": "", + "compute:detach_volume": "", + "compute:swap_volume": "", + + "compute:attach_interface": "", + "compute:detach_interface": "", + + "compute:set_admin_password": "", + + "compute:rescue": "", + "compute:unrescue": "", + + "compute:suspend": "", + "compute:resume": "", + + "compute:pause": "", + "compute:unpause": "", + + "compute:shelve": "", + "compute:shelve_offload": "", + "compute:unshelve": "", + + "compute:snapshot": "", + "compute:snapshot_volume_backed": "", + "compute:backup": "", + + "compute:resize": "", + "compute:confirm_resize": "", + "compute:revert_resize": "", + + "compute:rebuild": "", + "compute:reboot": "", + + "compute:security_groups:add_to_instance": "", + "compute:security_groups:remove_from_instance": "", + + "compute:delete": "", + "compute:soft_delete": "", + "compute:force_delete": "", + "compute:restore": "", + + "compute:volume_snapshot_create": "", + "compute:volume_snapshot_delete": "", + + "admin_api": "is_admin:True", + "compute_extension:accounts": "rule:admin_api", + "compute_extension:admin_actions": "rule:admin_api", + "compute_extension:admin_actions:pause": "rule:admin_or_owner", + "compute_extension:admin_actions:unpause": "rule:admin_or_owner", + "compute_extension:admin_actions:suspend": "rule:admin_or_owner", + "compute_extension:admin_actions:resume": "rule:admin_or_owner", + "compute_extension:admin_actions:lock": "rule:admin_or_owner", + "compute_extension:admin_actions:unlock": "rule:admin_or_owner", + "compute_extension:admin_actions:resetNetwork": "rule:admin_api", + "compute_extension:admin_actions:injectNetworkInfo": "rule:admin_api", + "compute_extension:admin_actions:createBackup": "rule:admin_or_owner", + "compute_extension:admin_actions:migrateLive": "rule:admin_api", + "compute_extension:admin_actions:resetState": "rule:admin_api", + "compute_extension:admin_actions:migrate": "rule:admin_api", + "compute_extension:aggregates": "rule:admin_api", + "compute_extension:agents": "rule:admin_api", + "compute_extension:attach_interfaces": "", + "compute_extension:baremetal_nodes": "rule:admin_api", + "compute_extension:cells": "rule:admin_api", + "compute_extension:cells:create": "rule:admin_api", + "compute_extension:cells:delete": "rule:admin_api", + "compute_extension:cells:update": "rule:admin_api", + "compute_extension:cells:sync_instances": "rule:admin_api", + "compute_extension:certificates": "", + "compute_extension:cloudpipe": "rule:admin_api", + "compute_extension:cloudpipe_update": "rule:admin_api", + "compute_extension:config_drive": "", + "compute_extension:console_output": "", + "compute_extension:consoles": "", + "compute_extension:createserverext": "", + "compute_extension:deferred_delete": "", + "compute_extension:disk_config": "", + "compute_extension:evacuate": "rule:admin_api", + "compute_extension:extended_server_attributes": "rule:admin_api", + "compute_extension:extended_status": "", + "compute_extension:extended_availability_zone": "", + "compute_extension:extended_ips": "", + "compute_extension:extended_ips_mac": "", + "compute_extension:extended_vif_net": "", + "compute_extension:extended_volumes": "", + "compute_extension:fixed_ips": "rule:admin_api", + "compute_extension:flavor_access": "", + "compute_extension:flavor_access:addTenantAccess": "rule:admin_api", + "compute_extension:flavor_access:removeTenantAccess": "rule:admin_api", + "compute_extension:flavor_disabled": "", + "compute_extension:flavor_rxtx": "", + "compute_extension:flavor_swap": "", + "compute_extension:flavorextradata": "", + "compute_extension:flavorextraspecs:index": "", + "compute_extension:flavorextraspecs:show": "", + "compute_extension:flavorextraspecs:create": "rule:admin_api", + "compute_extension:flavorextraspecs:update": "rule:admin_api", + "compute_extension:flavorextraspecs:delete": "rule:admin_api", + "compute_extension:flavormanage": "rule:admin_api", + "compute_extension:floating_ip_dns": "", + "compute_extension:floating_ip_pools": "", + "compute_extension:floating_ips": "", + "compute_extension:floating_ips_bulk": "rule:admin_api", + "compute_extension:fping": "", + "compute_extension:fping:all_tenants": "rule:admin_api", + "compute_extension:hide_server_addresses": "is_admin:False", + "compute_extension:hosts": "rule:admin_api", + "compute_extension:hypervisors": "rule:admin_api", + "compute_extension:image_size": "", + "compute_extension:instance_actions": "", + "compute_extension:instance_actions:events": "rule:admin_api", + "compute_extension:instance_usage_audit_log": "rule:admin_api", + "compute_extension:keypairs": "", + "compute_extension:keypairs:index": "", + "compute_extension:keypairs:show": "", + "compute_extension:keypairs:create": "", + "compute_extension:keypairs:delete": "", + "compute_extension:multinic": "", + "compute_extension:networks": "rule:admin_api", + "compute_extension:networks:view": "", + "compute_extension:networks_associate": "rule:admin_api", + "compute_extension:os-tenant-networks": "", + "compute_extension:quotas:show": "", + "compute_extension:quotas:update": "rule:admin_api", + "compute_extension:quotas:delete": "rule:admin_api", + "compute_extension:quota_classes": "", + "compute_extension:rescue": "", + "compute_extension:security_group_default_rules": "rule:admin_api", + "compute_extension:security_groups": "", + "compute_extension:server_diagnostics": "rule:admin_api", + "compute_extension:server_groups": "", + "compute_extension:server_password": "", + "compute_extension:server_usage": "", + "compute_extension:services": "rule:admin_api", + "compute_extension:shelve": "", + "compute_extension:shelveOffload": "rule:admin_api", + "compute_extension:simple_tenant_usage:show": "rule:admin_or_owner", + "compute_extension:simple_tenant_usage:list": "rule:admin_api", + "compute_extension:unshelve": "", + "compute_extension:users": "rule:admin_api", + "compute_extension:virtual_interfaces": "", + "compute_extension:virtual_storage_arrays": "", + "compute_extension:volumes": "", + "compute_extension:volume_attachments:index": "", + "compute_extension:volume_attachments:show": "", + "compute_extension:volume_attachments:create": "", + "compute_extension:volume_attachments:update": "", + "compute_extension:volume_attachments:delete": "", + "compute_extension:volumetypes": "", + "compute_extension:availability_zone:list": "", + "compute_extension:availability_zone:detail": "rule:admin_api", + "compute_extension:used_limits_for_admin": "rule:admin_api", + "compute_extension:migrations:index": "rule:admin_api", + "compute_extension:os-assisted-volume-snapshots:create": "rule:admin_api", + "compute_extension:os-assisted-volume-snapshots:delete": "rule:admin_api", + "compute_extension:console_auth_tokens": "rule:admin_api", + "compute_extension:os-server-external-events:create": "rule:admin_api", + + "network:get_all": "", + "network:get": "", + "network:create": "", + "network:delete": "", + "network:associate": "", + "network:disassociate": "", + "network:get_vifs_by_instance": "", + "network:allocate_for_instance": "", + "network:deallocate_for_instance": "", + "network:validate_networks": "", + "network:get_instance_uuids_by_ip_filter": "", + "network:get_instance_id_by_floating_address": "", + "network:setup_networks_on_host": "", + "network:get_backdoor_port": "", + + "network:get_floating_ip": "", + "network:get_floating_ip_pools": "", + "network:get_floating_ip_by_address": "", + "network:get_floating_ips_by_project": "", + "network:get_floating_ips_by_fixed_address": "", + "network:allocate_floating_ip": "", + "network:associate_floating_ip": "", + "network:disassociate_floating_ip": "", + "network:release_floating_ip": "", + "network:migrate_instance_start": "", + "network:migrate_instance_finish": "", + + "network:get_fixed_ip": "", + "network:get_fixed_ip_by_address": "", + "network:add_fixed_ip_to_instance": "", + "network:remove_fixed_ip_from_instance": "", + "network:add_network_to_project": "", + "network:get_instance_nw_info": "", + + "network:get_dns_domains": "", + "network:add_dns_entry": "", + "network:modify_dns_entry": "", + "network:delete_dns_entry": "", + "network:get_dns_entries_by_address": "", + "network:get_dns_entries_by_name": "", + "network:create_private_dns_domain": "", + "network:create_public_dns_domain": "", + "network:delete_dns_domain": "", + "network:attach_external_network": "rule:admin_api", + "network:get_vif_by_mac_address": "", + + "os_compute_api:servers:detail:get_all_tenants": "is_admin:True", + "os_compute_api:servers:index:get_all_tenants": "is_admin:True", + "os_compute_api:servers:confirm_resize": "", + "os_compute_api:servers:create": "", + "os_compute_api:servers:create:attach_network": "", + "os_compute_api:servers:create:attach_volume": "", + "os_compute_api:servers:create:forced_host": "rule:admin_api", + "os_compute_api:servers:delete": "", + "os_compute_api:servers:update": "", + "os_compute_api:servers:detail": "", + "os_compute_api:servers:index": "", + "os_compute_api:servers:reboot": "", + "os_compute_api:servers:rebuild": "", + "os_compute_api:servers:resize": "", + "os_compute_api:servers:revert_resize": "", + "os_compute_api:servers:show": "", + "os_compute_api:servers:create_image": "", + "os_compute_api:servers:create_image:allow_volume_backed": "", + "os_compute_api:servers:start": "rule:admin_or_owner", + "os_compute_api:servers:stop": "rule:admin_or_owner", + "os_compute_api:os-access-ips:discoverable": "", + "os_compute_api:os-access-ips": "", + "os_compute_api:os-admin-actions": "rule:admin_api", + "os_compute_api:os-admin-actions:discoverable": "", + "os_compute_api:os-admin-actions:reset_network": "rule:admin_api", + "os_compute_api:os-admin-actions:inject_network_info": "rule:admin_api", + "os_compute_api:os-admin-actions:reset_state": "rule:admin_api", + "os_compute_api:os-admin-password": "", + "os_compute_api:os-admin-password:discoverable": "", + "os_compute_api:os-aggregates:discoverable": "", + "os_compute_api:os-aggregates:index": "rule:admin_api", + "os_compute_api:os-aggregates:create": "rule:admin_api", + "os_compute_api:os-aggregates:show": "rule:admin_api", + "os_compute_api:os-aggregates:update": "rule:admin_api", + "os_compute_api:os-aggregates:delete": "rule:admin_api", + "os_compute_api:os-aggregates:add_host": "rule:admin_api", + "os_compute_api:os-aggregates:remove_host": "rule:admin_api", + "os_compute_api:os-aggregates:set_metadata": "rule:admin_api", + "os_compute_api:os-agents": "rule:admin_api", + "os_compute_api:os-agents:discoverable": "", + "os_compute_api:os-attach-interfaces": "", + "os_compute_api:os-attach-interfaces:discoverable": "", + "os_compute_api:os-baremetal-nodes": "rule:admin_api", + "os_compute_api:os-baremetal-nodes:discoverable": "", + "os_compute_api:os-block-device-mapping-v1:discoverable": "", + "os_compute_api:os-cells": "rule:admin_api", + "os_compute_api:os-cells:create": "rule:admin_api", + "os_compute_api:os-cells:delete": "rule:admin_api", + "os_compute_api:os-cells:update": "rule:admin_api", + "os_compute_api:os-cells:sync_instances": "rule:admin_api", + "os_compute_api:os-cells:discoverable": "", + "os_compute_api:os-certificates:create": "", + "os_compute_api:os-certificates:show": "", + "os_compute_api:os-certificates:discoverable": "", + "os_compute_api:os-cloudpipe": "rule:admin_api", + "os_compute_api:os-cloudpipe:discoverable": "", + "os_compute_api:os-config-drive": "", + "os_compute_api:os-consoles:discoverable": "", + "os_compute_api:os-consoles:create": "", + "os_compute_api:os-consoles:delete": "", + "os_compute_api:os-consoles:index": "", + "os_compute_api:os-consoles:show": "", + "os_compute_api:os-console-output:discoverable": "", + "os_compute_api:os-console-output": "", + "os_compute_api:os-remote-consoles": "", + "os_compute_api:os-remote-consoles:discoverable": "", + "os_compute_api:os-create-backup:discoverable": "", + "os_compute_api:os-create-backup": "rule:admin_or_owner", + "os_compute_api:os-deferred-delete": "", + "os_compute_api:os-deferred-delete:discoverable": "", + "os_compute_api:os-disk-config": "", + "os_compute_api:os-disk-config:discoverable": "", + "os_compute_api:os-evacuate": "rule:admin_api", + "os_compute_api:os-evacuate:discoverable": "", + "os_compute_api:os-extended-server-attributes": "rule:admin_api", + "os_compute_api:os-extended-server-attributes:discoverable": "", + "os_compute_api:os-extended-status": "", + "os_compute_api:os-extended-status:discoverable": "", + "os_compute_api:os-extended-availability-zone": "", + "os_compute_api:os-extended-availability-zone:discoverable": "", + "os_compute_api:extensions": "", + "os_compute_api:extension_info:discoverable": "", + "os_compute_api:os-extended-volumes": "", + "os_compute_api:os-extended-volumes:discoverable": "", + "os_compute_api:os-fixed-ips": "rule:admin_api", + "os_compute_api:os-fixed-ips:discoverable": "", + "os_compute_api:os-flavor-access": "", + "os_compute_api:os-flavor-access:discoverable": "", + "os_compute_api:os-flavor-access:remove_tenant_access": "rule:admin_api", + "os_compute_api:os-flavor-access:add_tenant_access": "rule:admin_api", + "os_compute_api:os-flavor-rxtx": "", + "os_compute_api:os-flavor-rxtx:discoverable": "", + "os_compute_api:flavors:discoverable": "", + "os_compute_api:os-flavor-extra-specs:discoverable": "", + "os_compute_api:os-flavor-extra-specs:index": "", + "os_compute_api:os-flavor-extra-specs:show": "", + "os_compute_api:os-flavor-extra-specs:create": "rule:admin_api", + "os_compute_api:os-flavor-extra-specs:update": "rule:admin_api", + "os_compute_api:os-flavor-extra-specs:delete": "rule:admin_api", + "os_compute_api:os-flavor-manage:discoverable": "", + "os_compute_api:os-flavor-manage": "rule:admin_api", + "os_compute_api:os-floating-ip-dns": "", + "os_compute_api:os-floating-ip-dns:discoverable": "", + "os_compute_api:os-floating-ip-dns:domain:update": "rule:admin_api", + "os_compute_api:os-floating-ip-dns:domain:delete": "rule:admin_api", + "os_compute_api:os-floating-ip-pools": "", + "os_compute_api:os-floating-ip-pools:discoverable": "", + "os_compute_api:os-floating-ips": "", + "os_compute_api:os-floating-ips:discoverable": "", + "os_compute_api:os-floating-ips-bulk": "rule:admin_api", + "os_compute_api:os-floating-ips-bulk:discoverable": "", + "os_compute_api:os-fping": "", + "os_compute_api:os-fping:discoverable": "", + "os_compute_api:os-fping:all_tenants": "rule:admin_api", + "os_compute_api:os-hide-server-addresses": "is_admin:False", + "os_compute_api:os-hide-server-addresses:discoverable": "", + "os_compute_api:os-hosts": "rule:admin_api", + "os_compute_api:os-hosts:discoverable": "", + "os_compute_api:os-hypervisors": "rule:admin_api", + "os_compute_api:os-hypervisors:discoverable": "", + "os_compute_api:images:discoverable": "", + "os_compute_api:image-size": "", + "os_compute_api:image-size:discoverable": "", + "os_compute_api:os-instance-actions": "", + "os_compute_api:os-instance-actions:discoverable": "", + "os_compute_api:os-instance-actions:events": "rule:admin_api", + "os_compute_api:os-instance-usage-audit-log": "rule:admin_api", + "os_compute_api:os-instance-usage-audit-log:discoverable": "", + "os_compute_api:ips:discoverable": "", + "os_compute_api:ips:index": "rule:admin_or_owner", + "os_compute_api:ips:show": "rule:admin_or_owner", + "os_compute_api:os-keypairs:discoverable": "", + "os_compute_api:os-keypairs": "", + "os_compute_api:os-keypairs:index": "rule:admin_api or user_id:%(user_id)s", + "os_compute_api:os-keypairs:show": "rule:admin_api or user_id:%(user_id)s", + "os_compute_api:os-keypairs:create": "rule:admin_api or user_id:%(user_id)s", + "os_compute_api:os-keypairs:delete": "rule:admin_api or user_id:%(user_id)s", + "os_compute_api:limits:discoverable": "", + "os_compute_api:limits": "", + "os_compute_api:os-lock-server:discoverable": "", + "os_compute_api:os-lock-server:lock": "rule:admin_or_owner", + "os_compute_api:os-lock-server:unlock": "rule:admin_or_owner", + "os_compute_api:os-lock-server:unlock:unlock_override": "rule:admin_api", + "os_compute_api:os-migrate-server:discoverable": "", + "os_compute_api:os-migrate-server:migrate": "rule:admin_api", + "os_compute_api:os-migrate-server:migrate_live": "rule:admin_api", + "os_compute_api:os-multinic": "", + "os_compute_api:os-multinic:discoverable": "", + "os_compute_api:os-networks": "rule:admin_api", + "os_compute_api:os-networks:view": "", + "os_compute_api:os-networks:discoverable": "", + "os_compute_api:os-networks-associate": "rule:admin_api", + "os_compute_api:os-networks-associate:discoverable": "", + "os_compute_api:os-pause-server:discoverable": "", + "os_compute_api:os-pause-server:pause": "rule:admin_or_owner", + "os_compute_api:os-pause-server:unpause": "rule:admin_or_owner", + "os_compute_api:os-pci:pci_servers": "", + "os_compute_api:os-pci:discoverable": "", + "os_compute_api:os-pci:index": "rule:admin_api", + "os_compute_api:os-pci:detail": "rule:admin_api", + "os_compute_api:os-pci:show": "rule:admin_api", + "os_compute_api:os-personality:discoverable": "", + "os_compute_api:os-preserve-ephemeral-rebuild:discoverable": "", + "os_compute_api:os-quota-sets:discoverable": "", + "os_compute_api:os-quota-sets:show": "rule:admin_or_owner", + "os_compute_api:os-quota-sets:defaults": "", + "os_compute_api:os-quota-sets:update": "rule:admin_api", + "os_compute_api:os-quota-sets:delete": "rule:admin_api", + "os_compute_api:os-quota-sets:detail": "rule:admin_api", + "os_compute_api:os-quota-class-sets:update": "rule:admin_api", + "os_compute_api:os-quota-class-sets:show": "is_admin:True or quota_class:%(quota_class)s", + "os_compute_api:os-quota-class-sets:discoverable": "", + "os_compute_api:os-rescue": "", + "os_compute_api:os-rescue:discoverable": "", + "os_compute_api:os-scheduler-hints:discoverable": "", + "os_compute_api:os-security-group-default-rules:discoverable": "", + "os_compute_api:os-security-group-default-rules": "rule:admin_api", + "os_compute_api:os-security-groups": "", + "os_compute_api:os-security-groups:discoverable": "", + "os_compute_api:os-server-diagnostics": "rule:admin_api", + "os_compute_api:os-server-diagnostics:discoverable": "", + "os_compute_api:os-server-password": "", + "os_compute_api:os-server-password:discoverable": "", + "os_compute_api:os-server-usage": "", + "os_compute_api:os-server-usage:discoverable": "", + "os_compute_api:os-server-groups": "", + "os_compute_api:os-server-groups:discoverable": "", + "os_compute_api:os-services": "rule:admin_api", + "os_compute_api:os-services:discoverable": "", + "os_compute_api:server-metadata:discoverable": "", + "os_compute_api:server-metadata:index": "rule:admin_or_owner", + "os_compute_api:server-metadata:show": "rule:admin_or_owner", + "os_compute_api:server-metadata:delete": "rule:admin_or_owner", + "os_compute_api:server-metadata:create": "rule:admin_or_owner", + "os_compute_api:server-metadata:update": "rule:admin_or_owner", + "os_compute_api:server-metadata:update_all": "rule:admin_or_owner", + "os_compute_api:servers:discoverable": "", + "os_compute_api:os-shelve:shelve": "", + "os_compute_api:os-shelve:shelve:discoverable": "", + "os_compute_api:os-shelve:shelve_offload": "rule:admin_api", + "os_compute_api:os-simple-tenant-usage:discoverable": "", + "os_compute_api:os-simple-tenant-usage:show": "rule:admin_or_owner", + "os_compute_api:os-simple-tenant-usage:list": "rule:admin_api", + "os_compute_api:os-suspend-server:discoverable": "", + "os_compute_api:os-suspend-server:suspend": "rule:admin_or_owner", + "os_compute_api:os-suspend-server:resume": "rule:admin_or_owner", + "os_compute_api:os-tenant-networks": "rule:admin_or_owner", + "os_compute_api:os-tenant-networks:discoverable": "", + "os_compute_api:os-shelve:unshelve": "", + "os_compute_api:os-user-data:discoverable": "", + "os_compute_api:os-virtual-interfaces": "", + "os_compute_api:os-virtual-interfaces:discoverable": "", + "os_compute_api:os-volumes": "", + "os_compute_api:os-volumes:discoverable": "", + "os_compute_api:os-volumes-attachments:index": "", + "os_compute_api:os-volumes-attachments:show": "", + "os_compute_api:os-volumes-attachments:create": "", + "os_compute_api:os-volumes-attachments:update": "", + "os_compute_api:os-volumes-attachments:delete": "", + "os_compute_api:os-volumes-attachments:discoverable": "", + "os_compute_api:os-availability-zone:list": "", + "os_compute_api:os-availability-zone:discoverable": "", + "os_compute_api:os-availability-zone:detail": "rule:admin_api", + "os_compute_api:os-used-limits": "rule:admin_api", + "os_compute_api:os-used-limits:discoverable": "", + "os_compute_api:os-migrations:index": "rule:admin_api", + "os_compute_api:os-migrations:discoverable": "", + "os_compute_api:os-assisted-volume-snapshots:create": "rule:admin_api", + "os_compute_api:os-assisted-volume-snapshots:delete": "rule:admin_api", + "os_compute_api:os-assisted-volume-snapshots:discoverable": "", + "os_compute_api:os-console-auth-tokens": "rule:admin_api", + "os_compute_api:os-server-external-events:create": "rule:admin_api" +} diff --git a/tricircle/cascade_service/compute.py b/tricircle/cascade_service/compute.py new file mode 100644 index 0000000..03e89b9 --- /dev/null +++ b/tricircle/cascade_service/compute.py @@ -0,0 +1,110 @@ +# Copyright 2015 Huawei Technologies Co., Ltd. +# +# 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. + +from nova.compute.manager import ComputeManager + +from nova.service import Service +import nova.version as nova_version +import nova.rpc as nova_rpc + +from tricircle.compute_tricircle.driver import TricircleComputeDriver +from tricircle.common.utils import get_import_path + +_REPORT_INTERVAL = 30 +_REPORT_INTERVAL_MAX = 60 + + +def _patch_nova_service(): + if (nova_version.loaded): + return + + nova_version.NOVA_PACKAGE = "tricircle" + nova_rpc.TRANSPORT.conf.set_override('control_exchange', 'nova') + nova_version.loaded = True + + +class NovaService(Service): + def __init__(self, *args, **kwargs): + _patch_nova_service() + self._conductor_api = None + self._rpcserver = None + super(NovaService, self).__init__(*args, **kwargs) + + @property + def conductor_api(self): + return self._conductor_api + + @conductor_api.setter + def conductor_api(self, value): + self._conductor_api = value + for client in ( + self._conductor_api.base_rpcapi.client, + self._conductor_api._manager.client, + ): + client.target.exchange = "nova" + + @property + def rpcserver(self): + return self._rpcserver + + @rpcserver.setter + def rpcserver(self, value): + self._rpcserver = value + if value is not None: + value.dispatcher._target.exchange = "nova" + + +def _fix_compute_service_exchange(service): + """Fix service exchange value for nova + """ + + manager = service.manager + for client in ( + manager.compute_rpcapi.client, + manager.compute_task_api.conductor_compute_rpcapi.client, + manager.consoleauth_rpcapi.client, + # manager.scheduler_client.queryclient.scheduler_rpcapi.client, + ): + client.target.exchange = "nova" + + +class ComputeHostManager(object): + def __init__(self, site_manager): + self._compute_nodes = [] + TricircleComputeDriver.site_manager = site_manager + + def _create_compute_node_service(self, host): + service = NovaService( + host=host, + binary="nova-compute", + topic="compute", # TODO(saggi): get from conf + db_allowed=False, + periodic_enable=True, + report_interval=_REPORT_INTERVAL, + periodic_interval_max=_REPORT_INTERVAL_MAX, + manager=get_import_path(ComputeManager), + compute_driver=get_import_path(TricircleComputeDriver), + ) + + _fix_compute_service_exchange(service) + + return service + + def create_host_adapter(self, host): + """Creates an adapter between the nova compute API and Site object + """ + service = self._create_compute_node_service(host) + service.start() + self._compute_nodes.append(service) diff --git a/tricircle/cascade_service/scheduler.py b/tricircle/cascade_service/scheduler.py new file mode 100644 index 0000000..912eb2c --- /dev/null +++ b/tricircle/cascade_service/scheduler.py @@ -0,0 +1,132 @@ +# Copyright 2015 Huawei Technologies Co., Ltd. +# +# 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. + +from socket import gethostname + +from oslo_config import cfg + +from nova import exception +from nova import objects +from nova.scheduler import driver +from nova.scheduler.manager import SchedulerManager + +from tricircle.common.utils import get_import_path + +from tricircle.cascade_service import site_manager +from tricircle.cascade_service.compute import NovaService + +cfg.CONF.import_opt('scheduler_topic', 'nova.scheduler.rpcapi') + +_REPORT_INTERVAL = 30 +_REPORT_INTERVAL_MAX = 60 + + +def _get_import_path(klass): + return "%s.%s" % (klass.__module__, klass.__name__) + + +def create_server(): + return NovaService( + host=gethostname(), + binary="nova-scheduler", + topic="scheduler", # TODO(saggi): get from conf + db_allowed=False, + periodic_enable=True, + report_interval=_REPORT_INTERVAL, + periodic_interval_max=_REPORT_INTERVAL_MAX, + manager=get_import_path(SchedulerManager), + scheduler_driver=get_import_path(TricircleSchedulerDriver), + ) + + +class _AvailabilityZone(object): + def __init__(self, name, host_manager): + self.name = name + self._host_manager = host_manager + self._site_manager = site_manager.get_instance() + + @property + def host_aggregates(self): + for aggregate in self._host_manager.aggregates: + if aggregate.metadata[u'availability_zone'] == self.name: + yield aggregate + + @property + def member_hosts(self): + for aggregate in self.host_aggregates: + for host in aggregate.hosts: + yield host + + @property + def valid_sites(self): + for host in self.member_hosts: + yield self._site_manager.get_site(host) + + +class _HostManager(object): + def __init__(self): + self.aggregates = [] + + # Required methods from OpenStack interface + + def update_aggregates(self, aggregates): + # This is not called reliably enough to trust + # we just reload the aggregates on every call + pass + + def delete_aggregate(self, aggregate): + # This is not called reliably enough to trust + # we just reload the aggregates on every call + pass + + def update_instance_info(self, context, host_name, instance_info): + pass + + def delete_instance_info(self, context, host_name, instance_uuid): + pass + + def sync_instance_info(self, context, host_name, instance_uuids): + pass + + # Tricircle only methods + + def get_availability_zone(self, az_name): + return _AvailabilityZone(az_name, self) + + def reload_aggregates(self, context): + self.aggregates = objects.AggregateList.get_all(context) + + +class TricircleSchedulerDriver(driver.Scheduler): + def __init__(self): + super(TricircleSchedulerDriver, self).__init__() + self.host_manager = _HostManager() + self._site_manager = site_manager.get_instance() + + def select_destinations(self, ctxt, request_spec, filter_properties): + self.host_manager.reload_aggregates(ctxt) + availability_zone = self.host_manager.get_availability_zone( + request_spec[u'instance_properties'][u'availability_zone']) + + for site in availability_zone.valid_sites: + site.prepare_for_instance(request_spec, filter_properties) + return [{ + 'host': site.name, + 'nodename': site.get_nodes()[0].hypervisor_hostname, + 'limits': None, + }] + else: + raise exception.NoValidHost( + "No sites match requested availability zone") diff --git a/tricircle/cascade_service/service.py b/tricircle/cascade_service/service.py index ce1a184..631fc9f 100644 --- a/tricircle/cascade_service/service.py +++ b/tricircle/cascade_service/service.py @@ -22,6 +22,7 @@ import oslo_messaging from tricircle.common import topics from tricircle.common.serializer import CascadeSerializer as Serializer +from tricircle.cascade_service import scheduler # import endpoints here from tricircle.cascade_service.endpoints.networking import ( @@ -42,7 +43,7 @@ class ServerControlEndpoint(object): self.server.stop() -def setup_server(): +def _create_main_cascade_server(): transport = oslo_messaging.get_transport(cfg.CONF) target = oslo_messaging.Target( exchange="tricircle", @@ -62,4 +63,11 @@ def setup_server(): serializer=Serializer(), ) server_control_endpoint.server = server + return server + + +def setup_server(): + scheduler_server = scheduler.create_server() + scheduler_server.start() + return _create_main_cascade_server() diff --git a/tricircle/cascade_service/site_manager.py b/tricircle/cascade_service/site_manager.py new file mode 100644 index 0000000..74a1ca2 --- /dev/null +++ b/tricircle/cascade_service/site_manager.py @@ -0,0 +1,123 @@ +# Copyright 2015 Huawei Technologies Co., Ltd. +# +# 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. +# TODO(saggi) change to oslo before release +from oslo_serialization import jsonutils as json + +from tricircle.common.singleton import Singleton +from tricircle.cascade_service.compute import ComputeHostManager + + +class Node(object): + def __init__(self, name): + self.vcpus = 20 + self.memory_mb = 1024 * 32 # 32 GB + self.memory_mb_used = self.memory_mb * 0.1 + self.free_ram_mb = self.memory_mb - self.memory_mb_used + self.local_gb = 1024 * 10 # 10 TB + self.local_gb_used = self.local_gb * 0.3 + self.free_disk_gb = self.local_gb - self.local_gb_used + self.vcpus_used = 0 + self.hypervisor_type = "Cascade Site" + self.hypervisor_version = 1 + self.current_workload = 1 + self.hypervisor_hostname = name + self.running_vms = 0 + self.cpu_info = "" + self.disk_available_least = 1 + self.supported_instances = [] + self.metrics = None + self.pci_stats = None + self.extra_resources = None + self.stats = json.dumps({}) + self.numa_topology = None + + def get_available_resource(self): + return { + "vcpus": self.vcpus, + "memory_mb": self.memory_mb, + "local_gb": self.local_gb, + "vcpus_used": self.vcpus_used, + "memory_mb_used": self.memory_mb_used, + "local_gb_used": self.local_gb_used, + "hypervisor_type": self.hypervisor_type, + "hypervisor_version": self.hypervisor_version, + "hypervisor_hostname": self.hypervisor_hostname, + "free_ram_mb": self.free_ram_mb, + "free_disk_gb": self.free_disk_gb, + "current_workload": self.current_workload, + "running_vms": self.running_vms, + "cpu_info": self.cpu_info, + "disk_available_least": self.disk_available_least, + "supported_instances": self.supported_instances, + "metrics": self.metrics, + "pci_stats": self.pci_stats, + "extra_resources": self.extra_resources, + "stats": (self.stats), + "numa_topology": (self.numa_topology), + } + + +class Site(object): + def __init__(self, name): + self.name = name + + # We currently just hold one aggregate subnode representing the + # resources owned by all the site's nodes. + self._aggragate_node = Node("cascade_" + name) + + self._instance_launch_information = {} + + def get_nodes(self): + return [self._aggragate_node] + + def get_node(self, name): + return self._aggragate_node + + def get_num_instances(self): + return 0 + + def prepare_for_instance(self, request_spec, filter_properties): + instance_uuid = request_spec[u'instance_properties']['uuid'] + self._instance_launch_information[instance_uuid] = ( + request_spec, + filter_properties + ) + + +class _SiteManager(object): + def __init__(self): + self._sites = {} + self.compute_host_manager = ComputeHostManager(self) + + # create fake data + # NOTE(saggi) replace with DAL access when available + self.create_site("Fake01") + self.create_site("Fake02") + + def create_site(self, site_name): + """creates a fake site, in reality the information about available + sites should be pulled from the DAL and not created at will. + """ + # TODO(saggi): thread safty + if site_name in self._sites: + raise RuntimeError("Site already exists in site map") + + self._sites[site_name] = Site(site_name) + self.compute_host_manager.create_host_adapter(site_name) + + def get_site(self, site_name): + return self._sites[site_name] + +get_instance = Singleton(_SiteManager).get_instance diff --git a/tricircle/common/rpc.py b/tricircle/common/rpc.py index 8c2c845..4c3036e 100644 --- a/tricircle/common/rpc.py +++ b/tricircle/common/rpc.py @@ -1,24 +1,33 @@ # Copyright 2015 Huawei Technologies Co., Ltd. -# 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 +# 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 +# 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. +# 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. + +from inspect import stack import neutron.common.rpc as neutron_rpc import neutron.common.topics as neutron_topics import neutron.context as neutron_context from oslo_config import cfg +from oslo_log import log as logging import oslo_messaging +from tricircle.common import topics +from tricircle.common.serializer import CascadeSerializer as Serializer + +TRANSPORT = oslo_messaging.get_transport(cfg.CONF) + +LOG = logging.getLogger(__name__) class NetworkingRpcApi(object): def __init__(self): @@ -46,3 +55,73 @@ class NetworkingRpcApi(object): call_context = self.client.prepare() return call_context.call(self._make_neutron_context(context), 'update_port_down', port_id=port_id) + + +def create_client(component_name): + topic = topics.CASCADING_SERVICE + target = oslo_messaging.Target( + exchange="tricircle", + topic=topic, + namespace=component_name, + version='1.0', + ) + + return oslo_messaging.RPCClient( + TRANSPORT, + target, + serializer=Serializer(), + ) + + +class AutomaticRpcWrapper(object): + def __init__(self, send_message_callback): + self._send_message = send_message_callback + + def _send_message(self, context, method, payload, cast=False): + """Cast the payload to the running cascading service instances.""" + + cctx = self._client.prepare( + fanout=cast, + ) + LOG.debug( + '%(what)s at %(topic)s.%(namespace)s the message %(method)s', + { + 'topic': cctx.target.topic, + 'namespace': cctx.target.namespace, + 'method': method, + 'what': {True: 'Fanout notify', False: 'Method call'}[cast], + } + ) + + if cast: + cctx.cast(context, method, payload=payload) + else: + return cctx.call(context, method, payload=payload) + + def send(self, cast): + """ Autowrap an API call with a send_message() call + + This function uses python tricks to implement a passthrough call from + the calling API to the cascade service + """ + caller = stack()[1] + frame = caller[0] + method_name = caller[3] + context = frame.f_locals.get('context', {}) + + payload = {} + for varname in frame.f_code.co_varnames: + if varname in ("self", "context"): + continue + + try: + payload[varname] = frame.f_locals[varname] + except KeyError: + pass + + LOG.info( + "Farwarding request to %s(%s)", + method_name, + payload, + ) + return self._send_message(context, method_name, payload, cast) diff --git a/tricircle/common/singleton.py b/tricircle/common/singleton.py new file mode 100644 index 0000000..c8d599d --- /dev/null +++ b/tricircle/common/singleton.py @@ -0,0 +1,30 @@ +# Copyright 2015 Huawei Technologies Co., Ltd. +# +# 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. +from threading import Lock + + +class Singleton(object): + def __init__(self, factory_method): + self._factory_method = factory_method + self._instance = None + self._instanceLock = Lock() + + def get_instance(self): + if self._instance is None: + with self._instanceLock: + if self._instance is None: + self._instance = self._factory_method() + + return self._instance diff --git a/tricircle/common/utils.py b/tricircle/common/utils.py new file mode 100644 index 0000000..5201cee --- /dev/null +++ b/tricircle/common/utils.py @@ -0,0 +1,18 @@ +# Copyright 2015 Huawei Technologies Co., Ltd. +# +# 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. + + +def get_import_path(cls): + return cls.__module__ + "." + cls.__name__ diff --git a/tricircle/compute_tricircle/__init__.py b/tricircle/compute_tricircle/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tricircle/compute_tricircle/driver.py b/tricircle/compute_tricircle/driver.py new file mode 100644 index 0000000..6b9767a --- /dev/null +++ b/tricircle/compute_tricircle/driver.py @@ -0,0 +1,64 @@ +# Copyright 2015 Huawei Technologies Co., Ltd. +# +# 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. + +from nova.virt import driver +from nova.virt.hardware import InstanceInfo +import nova.compute.power_state as power_state + +from oslo_config import cfg +from oslo_log import log as logging +import oslo_messaging + +TRANSPORT = oslo_messaging.get_transport(cfg.CONF) + +LOG = logging.getLogger(__name__) + + +class TricircleComputeDriver(driver.ComputeDriver): + site_manager = None # will be set later by the ComputeHostManager + + def __init__(self, virtapi): + super(TricircleComputeDriver, self).__init__(virtapi) + + def init_host(self, host): + self.host = host + # NOTE(saggi) There is no way to pass arguments to the driver apart + # from the host It's a bit convoluted and if you find a better way + # please send a patch + self._site = TricircleComputeDriver.site_manager.get_site(host) + + def get_available_nodes(self, refresh=False): + return [node.hypervisor_hostname for node in self._site.get_nodes()] + + def get_available_resource(self, nodename): + return self._site.get_node(nodename).get_available_resource() + + def get_num_instances(self): + return self._site.get_num_instances() + + def spawn(self, context, instance, image_meta, injected_files, + admin_password, network_info=None, block_device_info=None): + pass + + def get_info(self, instance): + # TODO(saggi) will be redirected to cascade service + return InstanceInfo( + state=power_state.RUNNING, + max_mem_kb=500, + mem_kb=500, + num_cpu=1, + cpu_time_ns=100, + id=instance.id, + )