├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTOR ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── RMD_VERSION ├── cmd ├── gen_conf │ ├── gen_conf.go │ └── template │ │ ├── broadwell.go │ │ ├── skylake.go │ │ ├── template_openstack.go │ │ └── template_standard.go └── rmd │ ├── rmd.go │ └── server.go ├── deb.json ├── docs ├── ConfigurationGuide.md ├── DeveloperQuickStart.md ├── PluginDevelopmentGuide.md ├── Prerequisite.md ├── UserGuide.md ├── api │ └── v1 │ │ └── swagger.yaml ├── pic │ ├── Workload-DELETE.png │ ├── Workload-POST.png │ ├── forking.png │ ├── initialized-modules.png │ ├── module-flow.png │ ├── rest-request-flow.png │ ├── rmd_logical_view.png │ └── rmd_pools.png └── reference │ ├── README.md │ ├── libpqos-user-interface.txt │ └── linux-kernel-user-interface.txt ├── etc └── rmd │ ├── acl │ ├── roles │ │ ├── admin │ │ │ └── cert.pem │ │ └── user │ │ │ └── user-cert.pem │ └── url │ │ ├── model.conf │ │ └── policy.csv │ ├── cert │ ├── client │ │ ├── ca.pem │ │ ├── cert.pem │ │ ├── key.pem │ │ ├── user-cert.pem │ │ └── user-key.pem │ └── server │ │ ├── ca.pem │ │ ├── rmd-cert.pem │ │ └── rmd-key.pem │ ├── cpu_map.toml │ ├── pam │ ├── rmd │ └── test │ │ └── rmd │ ├── policy.toml │ ├── policy.yaml │ └── rmd.toml ├── external └── sampleplugin │ ├── Makefile │ ├── SamplePluginUG.md │ ├── interface.go │ ├── internal.go │ └── rest.go ├── go.mod ├── go.sum ├── internal ├── db │ ├── boltdb.go │ ├── boltdb_test.go │ ├── config │ │ └── config.go │ ├── db.go │ └── mgodb.go ├── error │ ├── error.go │ └── error_test.go ├── inventory │ ├── inventory.go │ ├── pstate.go │ └── rdt_cat.go ├── openstack │ ├── config.go │ ├── notifhandler.go │ ├── novalistener.go │ ├── openstack_disabled.go │ ├── openstack_enabled.go │ ├── openstackclient.go │ ├── openstackclient_test.go │ ├── providercfg.go │ ├── util.go │ └── util_test.go ├── pam │ ├── callback.go │ ├── config │ │ └── config.go │ ├── pam.go │ ├── pam_test.go │ ├── transaction.c │ └── transaction.go ├── plugins │ ├── handler.go │ ├── handler_test.go │ ├── loader.go │ └── modinterface.go └── proxy │ ├── client │ ├── client.go │ ├── generic.go │ ├── pam.go │ └── resctrl.go │ ├── server │ ├── generic.go │ ├── pam.go │ ├── resctrl.go │ └── server.go │ └── types │ ├── pam.go │ ├── pipepair.go │ └── resctrl.go ├── modules ├── cache │ ├── cache.go │ ├── cachepool.go │ ├── caches_api.go │ ├── config │ │ └── config.go │ ├── cpupool.go │ ├── infragroup.go │ ├── osgroup.go │ └── rdtpool.go ├── hospitality │ ├── hospitality.go │ ├── hospitality_api.go │ └── hospitality_test.go ├── mba │ ├── api.go │ ├── mba.go │ ├── mba_api.go │ └── mba_test.go ├── policy │ ├── policy.go │ ├── policy_api.go │ ├── policy_test.go │ └── policy_test_file.yaml └── workload │ ├── types │ └── types.go │ ├── workload.go │ ├── workload_api.go │ └── workload_test.go ├── packaging ├── download_extra_packages.sh └── rmd.spec ├── rmd.8 ├── rpm.json ├── scripts ├── build-opts-get ├── build.sh ├── deps.sh ├── go-env ├── hacking.sh ├── hacking_v2.sh ├── install.sh ├── prerm.sh ├── rmd.service ├── setup_pam_files.sh ├── setup_rmd_users.sh └── test.sh ├── test ├── integration │ ├── caches_test.go │ ├── integration.go │ ├── integration_suite_test.go │ ├── policy_test.go │ └── workload_test.go ├── integrationHTTPS │ ├── cert_https_auth_test.go │ ├── pam_https_auth_test.go │ └── suite_test.go └── test_helpers │ ├── schema.go │ ├── utils.go │ └── utils_test.go ├── utils ├── acl │ ├── acl.go │ └── config │ │ └── config.go ├── auth │ └── pam.go ├── bitmap │ ├── bitmap.go │ ├── bitmap_test.go │ ├── bitmaputil.go │ └── bitmaputil_test.go ├── bootcheck │ └── bootcheck.go ├── config │ └── config.go ├── cpu │ ├── cpu.go │ ├── cpu_test.go │ └── microarch.go ├── flag │ └── flags.go ├── flock │ ├── flock.go │ ├── flock_suite_test.go │ └── flock_test.go ├── log │ ├── config │ │ └── config.go │ └── log.go ├── pidfile │ └── pidfile.go ├── pqos │ ├── pqos.go │ ├── pqos_test.go │ └── pqos_wrapper.c ├── proc │ ├── proc.go │ └── proc_test.go ├── resctrl │ ├── resctrl.go │ ├── resctrl_test.go │ └── task_flow.go ├── task │ └── task.go ├── tls │ └── tls.go ├── util.go └── util_test.go └── version └── version.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.exe 3 | *.o 4 | *.a 5 | *.deb 6 | *.rpm 7 | .idea/ 8 | vendor/ 9 | build/ 10 | pkg-build/ 11 | go.sum 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | os: 3 | - linux 4 | addons: 5 | apt: 6 | packages: 7 | - fakeroot 8 | - openssl 9 | - libpam0g-dev 10 | - db-util 11 | - git 12 | 13 | go: 14 | - "1.13" 15 | - "1.14" 16 | - "1.15" 17 | - "tip" 18 | 19 | before_install: 20 | - if [ "${JOB}" = "check" ]; then go get -v golang.org/x/lint/golint; fi 21 | - ( git clone --depth=1 https://github.com/intel/intel-cmt-cat /tmp/pqos && cd /tmp/pqos && make && sudo make install ) 22 | 23 | script: 24 | - 'if [ "${JOB}" = "check" ]; then make check; fi' 25 | - 'if [ "${JOB}" = "build" ]; then make; fi' 26 | - 'if [ "${JOB}" = "docker" ]; then make docker; fi' 27 | 28 | env: 29 | global: 30 | - GOARCH=amd64 31 | - GO111MODULE=on 32 | jobs: 33 | - JOB=check 34 | - JOB=build 35 | - JOB=docker 36 | 37 | jobs: 38 | allow_failures: 39 | - go: "1.12" 40 | env: JOB=check 41 | - go: "1.13" 42 | env: JOB=check 43 | - go: "1.14" 44 | env: JOB=check 45 | - go: "tip" 46 | env: JOB=check 47 | 48 | notifications: 49 | email: 50 | on_success: change 51 | on_failure: always 52 | 53 | services: 54 | - docker 55 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Resource Management Daemon changelog 2 | 3 | ## Version 0.3 4 | 5 | ### Bugfixing and design change 6 | 7 | List of changes 8 | - add support for MBA 9 | - enabling usage of external plugins 10 | - update documentation 11 | - cleaned code and fixed bugs 12 | - Docker file update 13 | - fixes for Fedora packaging review 14 | 15 | ## Version 0.2 16 | 17 | ### Bugfixing and design change 18 | 19 | List of changes 20 | - project changed to go module (Go >= 1.11 required) 21 | - code restructured for modular architecture 22 | - updated travis and rpm packaging files 23 | - refactored workload and policy related modules 24 | - introduced modular interface for resource plugins 25 | - updated unit and functional tests 26 | - implemented PAM library wrapper 27 | - RMD TLS connection forced to use AES-256/SHA-384 cipher suite 28 | - cleaned code and fixed bugs 29 | 30 | ## Version 0.1 31 | 32 | ### Initial release 33 | -------------------------------------------------------------------------------- /CONTRIBUTOR: -------------------------------------------------------------------------------- 1 | RMD contributor 2 | =============== 3 | 4 | Feng, Shaohe 5 | Dakshina Ilangovan 6 | Qiao, Liyong 7 | Lin, Yang 8 | ArunPrabhu 9 | Marcin Kaminski 10 | Michal Lorenc 11 | Piotr Kleski 12 | Shashank T D 13 | 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #Copyright 2017 Intel Corporation 2 | # 3 | #Licensed under the Apache License, Version 2.0 (the "License"); 4 | #you may not use this file except in compliance with the License. 5 | #You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | #Unless required by applicable law or agreed to in writing, software 10 | #distributed under the License is distributed on an "AS IS" BASIS, 11 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | #See the License for the specific language governing permissions and 13 | #limitations under the License. 14 | 15 | # Start from a Debian image with the latest version of Go installed 16 | # and a workspace (GOPATH) configured at /go. 17 | FROM golang:1.13 18 | # Pull intel-cmt-cat 19 | RUN mkdir -p /home/intel-cmt-cat \ 20 | cd /home/intel-cmt-cat 21 | RUN git clone https://github.com/intel/intel-cmt-cat.git 22 | WORKDIR /go/intel-cmt-cat 23 | RUN make install 24 | # Copy the local package files to the container's workspace. 25 | WORKDIR /go/src/github.com/intel/rmd 26 | COPY . . 27 | 28 | #Add proxy settings below if behind a proxy. 29 | #ENV http_proxy= 30 | #ENV https_proxy= 31 | #ENV ftp_proxy= 32 | #ENV socks_proxy= 33 | #ENV no_proxy= 34 | 35 | RUN apt update && apt install openssl libpam0g-dev db-util -y && \ 36 | rm -rf /var/lib/apt/lists/* 37 | 38 | RUN make install && make clean 39 | 40 | #RUN mount -t resctrl resctrl /sys/fs/resctrl 41 | # what etc should we use? 42 | # log 43 | 44 | # Run the outyet command by default when the container starts. 45 | ENTRYPOINT ["/usr/bin/rmd","-d","--address","0.0.0.0"] 46 | 47 | # Document that the service listens on port 8080. 48 | #EXPOSE 8081 49 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OS = $(shell uname -s | tr '[:upper:]' '[:lower:]') 2 | ARCH = $(shell uname -m) 3 | RMD_VERSION = $(shell cat RMD_VERSION | sed -e 's/^v//') 4 | OUTPUT_DIR=build/$(OS)/$(ARCH) 5 | RMD_DIR = rmd-$(RMD_VERSION) 6 | go-bin-deb = $(GOPATH)/bin/go-bin-deb 7 | go-bin-rpm = $(GOPATH)/bin/go-bin-rpm 8 | 9 | BUILD_TYPE ?= standard 10 | 11 | .PHONY: build check clean deb deps docker install rpm test-func test-unit pstate pstateinstall 12 | 13 | default: deps build 14 | 15 | deps: 16 | bash -c "./scripts/deps.sh" 17 | check: 18 | bash -c "./scripts/hacking_v2.sh" 19 | test-unit: deps 20 | bash -c "./scripts/test.sh -u -b $(BUILD_TYPE)" 21 | test-func: deps build 22 | bash -c "./scripts/test.sh -f" 23 | build: deps 24 | bash -c "./scripts/build.sh -b $(BUILD_TYPE)" 25 | deb: build 26 | ifeq (x86_64, $(ARCH)) 27 | @$(go-bin-deb) generate --arch amd64 --version $(PACKAGE_VERSION) 28 | else 29 | @$(go-bin-deb) generate --arch $(ARCH) --version $(PACKAGE_VERSION) 30 | endif 31 | rpm: build 32 | @$(go-bin-rpm) generate --arch $(ARCH) --version $(PACKAGE_VERSION) --output rmd-$(PACKAGE_VERSION).$(ARCH).rpm 33 | install: build 34 | mkdir -p $(DESTDIR)/usr/bin 35 | cp $(OUTPUT_DIR)/rmd $(DESTDIR)/usr/bin/rmd 36 | cp $(OUTPUT_DIR)/gen_conf $(DESTDIR)/usr/bin/gen_conf 37 | bash -c "./scripts/install.sh --skip-pam-userdb" 38 | package: 39 | mkdir -p RMD_DIR 40 | rsync -avr --exclude=$(RMD_DIR),.git,build * $(RMD_DIR) 41 | tar -zcvf $(RMD_DIR).tar.gz $(RMD_DIR)/ 42 | rm -rf $(RMD_DIR)/ 43 | rpmdev-setuptree 44 | cp packaging/rmd.spec $(HOME)/rpmbuild/SPECS 45 | mv $(RMD_DIR).tar.gz $(HOME)/rpmbuild/SOURCES 46 | bash -c "./packaging/download_extra_packages.sh $(PWD)/packages/ $(HOME)/rpmbuild/SOURCES" 47 | rpmbuild -ba $(HOME)/rpmbuild/SPECS/rmd.spec 48 | docker: 49 | @docker build -t rmd . 50 | clean: 51 | rm -rf build pkg-build 52 | 53 | -------------------------------------------------------------------------------- /RMD_VERSION: -------------------------------------------------------------------------------- 1 | v0.3.1 2 | -------------------------------------------------------------------------------- /cmd/gen_conf/gen_conf.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" // flag is enough for us. 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "strings" 10 | 11 | "github.com/intel/rmd/cmd/gen_conf/template" 12 | testhelpers "github.com/intel/rmd/test/test_helpers" // TODO: should move it to source path 13 | "github.com/intel/rmd/utils/cpu" 14 | ) 15 | 16 | var gopherType string 17 | 18 | const ( 19 | confPath = "./rmd.toml" 20 | defPlatform = "Broadwell" 21 | ) 22 | 23 | func genDefaultPlatForm() string { 24 | dpf := strings.Title(cpu.GetMicroArch(cpu.GetSignature())) 25 | if dpf == "" { 26 | fmt.Println("Do not support the host platform, use defaut platform:", defPlatform) 27 | dpf = defPlatform 28 | } 29 | return dpf 30 | } 31 | 32 | func genPlatFormMap() map[string]bool { 33 | m := cpu.NewCPUMap() 34 | pfm := map[string]bool{} 35 | for _, value := range m { 36 | pfm[strings.Title(value)] = true 37 | } 38 | return pfm 39 | } 40 | 41 | func genPlatFormList(pfm map[string]bool) []string { 42 | pfs := []string{} 43 | for k := range pfm { 44 | pfs = append(pfs, k) 45 | } 46 | return pfs 47 | } 48 | 49 | func mergeOptions(options ...map[string]interface{}) map[string]interface{} { 50 | union := make(map[string]interface{}) 51 | for _, o := range options { 52 | for k, v := range o { 53 | union[k] = v 54 | } 55 | } 56 | return union 57 | } 58 | 59 | func main() { 60 | path := flag.String("path", confPath, "the path of the generated rmd config.") 61 | 62 | dpf := genDefaultPlatForm() 63 | pfm := genPlatFormMap() 64 | pfs := genPlatFormList(pfm) 65 | platform := flag.String("platform", dpf, 66 | "the platform than rmd will run, Support PlatForm:\n\t "+strings.Join(pfs, ", ")) 67 | datas := flag.String("data", "", 68 | "Data options that overwrite the config opitons. "+ 69 | "It can be a json format as '{\"a\": 1, \"b\": 2}' or "+ 70 | "a key/value assignment format as \"key=value,key=value\". "+ 71 | "It can also start a letter @, the rest should be a file name to read the json data from.") 72 | flag.Parse() 73 | 74 | if _, ok := pfm[*platform]; !ok { 75 | fmt.Println("Error, unsupport platform:", *platform) 76 | os.Exit(1) 77 | } 78 | 79 | union := []map[string]interface{}{template.Options} 80 | 81 | // Skylake, Kaby Lake, Broadwell 82 | // FIXME hard code, a smart way to load the platform and other optionts automatically. 83 | if *platform == "Broadwell" { 84 | union = append(union, template.Broadwell) 85 | } 86 | if *platform == "Skylake" { 87 | union = append(union, template.Skylake) 88 | } 89 | 90 | var bdatas []byte 91 | if strings.HasPrefix(*datas, "@") { 92 | f := strings.TrimPrefix(*datas, "@") 93 | dat, err := ioutil.ReadFile(f) 94 | if err != nil { 95 | fmt.Println("Bad data file: ", err) 96 | os.Exit(1) 97 | } 98 | bdatas = dat 99 | } else if strings.HasPrefix(*datas, "{") { 100 | bdatas = []byte(*datas) 101 | } else if strings.Contains(*datas, "=") { 102 | // TODO will also support -data 'stdout=true,tasks=["ovs*",dpdk]' 103 | fmt.Println("Unsupport key/value assignment format at present.") 104 | os.Exit(1) 105 | } 106 | if len(bdatas) > 2 { 107 | var js interface{} 108 | if err := json.Unmarshal([]byte(bdatas), &js); err != nil { 109 | fmt.Println("Error to paser data:", err) 110 | os.Exit(1) 111 | } 112 | union = append(union, js.(map[string]interface{})) 113 | } 114 | 115 | option := mergeOptions(union...) 116 | conf, err := testhelpers.FormatByKey(template.Templ, option) 117 | if err != nil { 118 | fmt.Println("Error, to generate config file:", err) 119 | os.Exit(1) 120 | } 121 | 122 | f, err := os.Create(*path) 123 | if err != nil { 124 | fmt.Println("Error, to create config file:", err) 125 | os.Exit(1) 126 | } 127 | defer f.Close() 128 | _, err = f.WriteString(conf) 129 | if err != nil { 130 | fmt.Println("Error, to write config file:", err) 131 | os.Exit(1) 132 | } 133 | f.Sync() 134 | } 135 | -------------------------------------------------------------------------------- /cmd/gen_conf/template/broadwell.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | // Broadwell options 4 | var Broadwell = map[string]interface{}{ 5 | "os_cacheways": 1, 6 | "infra_cacheways": 19, 7 | "max_shared": 10, 8 | "guarantee": 10, 9 | "besteffort": 7, 10 | "shared": 2} 11 | -------------------------------------------------------------------------------- /cmd/gen_conf/template/skylake.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | // Skylake options 4 | var Skylake = map[string]interface{}{ 5 | "os_cacheways": 1, 6 | "infra_cacheways": 10, 7 | "max_shared": 10, 8 | "guarantee": 6, 9 | "besteffort": 3, 10 | "shared": 1} 11 | -------------------------------------------------------------------------------- /cmd/gen_conf/template/template_openstack.go: -------------------------------------------------------------------------------- 1 | // +build openstack 2 | 3 | // Package template TODO: This template is too sample. need to improve it. 4 | // Not sure golang native template can satify us. 5 | package template 6 | 7 | // Options are options for temlate will be replaced 8 | var Options = map[string]interface{}{ 9 | "address": "localhost", 10 | "debugport": 8081, 11 | "tlsport": 8443, 12 | "clientauth": "challenge", 13 | "logfile": "/var/log/rmd.log", 14 | "dbbackend": "bolt", 15 | "dbtransport": "/var/run/rmd.db", 16 | "logtostdout": true, 17 | "os_cacheways": 1, 18 | "infra_cacheways": 19, 19 | "max_shared": 10, 20 | "guarantee": 10, 21 | "besteffort": 7, 22 | "shared": 2, 23 | "shrink": false, 24 | "policypath": "/etc/rmd/policy.toml", 25 | "sysresctrl": "/sys/fs/resctrl", 26 | "plugins": "", 27 | "providerConfigPath": "/etc/nova/provider_config/rmd.yaml", 28 | "amqpuri": "amqp://test:test@localhost:5672/", 29 | "bindingKey": "versioned_notifications.info", 30 | "keystoneUrl": "http://10.237.214.102/identity/v3/auth/tokens", 31 | "keystoneLogin": "admin", 32 | "keystonePassword": "nimda", 33 | "dbValidatorInterval": 30, //seconds 34 | "mbaMode": "percentage", 35 | } 36 | 37 | // Templ is content of template 38 | const Templ = `# This is a rmd config. 39 | 40 | title = "RMD Config" 41 | 42 | [default] 43 | address = "{{.address}}" 44 | policypath = "{{.policypath}}" 45 | sysresctrl = "{{.sysresctrl}}" 46 | tlsport = {{.tlsport}} 47 | # certpath = "/etc/rmd/cert/server" # Only support pem format, hard code that CAFile is ca.pem, CertFile is rmd-cert.pem, KeyFile is rmd-key.pem 48 | # clientcapath = "/etc/rmd/cert/client" # Only support pem format, hard code that CAFile is ca.pem 49 | clientauth = "{{.clientauth}}" # can be "no, require, require_any, challenge_given, challenge", challenge means require and verify. 50 | # unixsock = "/var/run/rmd.sock" 51 | plugins = "{{.plugins}}" # comma separated list of enabled RMD plugins, for each plugin (ex. PLUGINX) appropriate config section (ex. [PLUGINX]) is needed 52 | openstackenable = true 53 | dbValidatorInterval = {{.dbValidatorInterval}} 54 | 55 | [rdt] 56 | mbaMode = "{{.mbaMode}}" # MBA mode of operation, possible options are: "none", "percentage" and "mbps" 57 | 58 | [log] 59 | path = "{{.logfile}}" 60 | env = "dev" # production or dev 61 | level = "debug" 62 | stdout = {{.logtostdout}} 63 | 64 | [database] 65 | backend = "{{.dbbackend}}" 66 | transport = "{{.dbtransport}}" 67 | dbname = "bolt" 68 | 69 | [debug] 70 | enabled = false 71 | debugport = {{.debugport}} 72 | 73 | [OSGroup] # OSGroup is mandatory 74 | cacheways = {{.os_cacheways}} 75 | cpuset = "0-1" 76 | 77 | [InfraGroup] # InfraGroup is optional 78 | cacheways = {{.infra_cacheways}} 79 | cpuset = "2-3" 80 | tasks = ["ovs*"] # Just support Wildcards. Do we need to support RE? 81 | 82 | [CachePool] # Cache Pool config is optional 83 | shrink = {{.shrink}} 84 | max_allowed_shared = {{.max_shared}} # max allowed workload in shared pool, default is {{.max_shared}} 85 | guarantee = {{.guarantee}} 86 | besteffort = {{.besteffort}} 87 | shared = {{.shared}} 88 | 89 | [acl] 90 | path = "/etc/rmd/acl/" # 91 | # use CSV format 92 | filter = "url" # at present just support "url", will support "ip, proto" 93 | authorization = "signature" # authorize the client, can identify client by signature, role(OU) or username(CN). Default value is signature. If value is signature, admincert and usercert should be set. 94 | admincert = "/etc/rmd/acl/roles/admin/" # A cert is used to describe user info. These cert files in this path are used to define the users that are admin. Only pem format file at present. The files can be updated dynamicly. 95 | usercert = "/etc/rmd/acl/roles/user/" # A cert is used to describe user info. These cert files in this path are used to define the user with low privilege. Only pem format file at present. The files can be updated dynamicly. 96 | 97 | [pam] 98 | service = "rmd" 99 | 100 | [openstack] 101 | # Path below is optional. If not given then file will not be generated 102 | #providerConfigPath = "{{.providerConfigPath}}" 103 | #amqpuri = "{{.amqpuri}}" # URI to openstack amqp server, required to subscribe to nova notifications 104 | #bindingKey = "{{.bindingKey}}" 105 | #keystoneUrl = "{{.keystoneUrl}}" # URI to Keystone server, required to get token 106 | #keystoneLogin = "{{.keystoneLogin}}" 107 | #keystonePassword = "{{.keystonePassword}}" 108 | ` 109 | -------------------------------------------------------------------------------- /cmd/gen_conf/template/template_standard.go: -------------------------------------------------------------------------------- 1 | // +build !openstack 2 | 3 | // Package template TODO: This template is too sample. need to improve it. 4 | // Not sure golang native template can satify us. 5 | package template 6 | 7 | // Options are options for temlate will be replaced 8 | var Options = map[string]interface{}{ 9 | "address": "localhost", 10 | "debugport": 8081, 11 | "tlsport": 8443, 12 | "clientauth": "challenge", 13 | "logfile": "/var/log/rmd.log", 14 | "dbbackend": "bolt", 15 | "dbtransport": "/var/run/rmd.db", 16 | "logtostdout": true, 17 | "os_cacheways": 1, 18 | "infra_cacheways": 19, 19 | "max_shared": 10, 20 | "guarantee": 10, 21 | "besteffort": 7, 22 | "shared": 2, 23 | "shrink": false, 24 | "policypath": "/etc/rmd/policy.toml", 25 | "sysresctrl": "/sys/fs/resctrl", 26 | "plugins": "", 27 | "dbValidatorInterval": 30, //seconds 28 | "mbaMode": "percentage", 29 | } 30 | 31 | // Templ is content of template 32 | const Templ = `# This is a rmd config. 33 | 34 | title = "RMD Config" 35 | 36 | [default] 37 | address = "{{.address}}" 38 | policypath = "{{.policypath}}" 39 | sysresctrl = "{{.sysresctrl}}" 40 | tlsport = {{.tlsport}} 41 | # certpath = "/etc/rmd/cert/server" # Only support pem format, hard code that CAFile is ca.pem, CertFile is rmd-cert.pem, KeyFile is rmd-key.pem 42 | # clientcapath = "/etc/rmd/cert/client" # Only support pem format, hard code that CAFile is ca.pem 43 | clientauth = "{{.clientauth}}" # can be "no, require, require_any, challenge_given, challenge", challenge means require and verify. 44 | # unixsock = "/var/run/rmd.sock" 45 | plugins = "{{.plugins}}" # comma separated list of enabled RMD plugins, for each plugin (ex. PLUGINX) appropriate config section (ex. [PLUGINX]) is needed 46 | dbValidatorInterval = {{.dbValidatorInterval}} 47 | 48 | [rdt] 49 | mbaMode = "{{.mbaMode}}" # MBA mode of operation, possible options are: "none", "percentage" and "mbps" 50 | 51 | [log] 52 | path = "{{.logfile}}" 53 | env = "dev" # production or dev 54 | level = "debug" 55 | stdout = {{.logtostdout}} 56 | 57 | [database] 58 | backend = "{{.dbbackend}}" # mgo 59 | transport = "{{.dbtransport}}" # mongodb://localhost 60 | dbname = "bolt" 61 | 62 | [debug] 63 | enabled = false 64 | debugport = {{.debugport}} 65 | 66 | [OSGroup] # OSGroup is mandatory 67 | cacheways = {{.os_cacheways}} 68 | cpuset = "0-1" 69 | 70 | [InfraGroup] # InfraGroup is optional 71 | cacheways = {{.infra_cacheways}} 72 | cpuset = "2-3" 73 | tasks = ["ovs*"] # Just support Wildcards. Do we need to support RE? 74 | 75 | [CachePool] # Cache Pool config is optional 76 | shrink = {{.shrink}} 77 | max_allowed_shared = {{.max_shared}} # max allowed workload in shared pool, default is {{.max_shared}} 78 | guarantee = {{.guarantee}} 79 | besteffort = {{.besteffort}} 80 | shared = {{.shared}} 81 | 82 | [acl] 83 | path = "/etc/rmd/acl/" # 84 | # use CSV format 85 | filter = "url" # at present just support "url", will support "ip, proto" 86 | authorization = "signature" # authorize the client, can identify client by signature, role(OU) or username(CN). Default value is signature. If value is signature, admincert and usercert should be set. 87 | admincert = "/etc/rmd/acl/roles/admin/" # A cert is used to describe user info. These cert files in this path are used to define the users that are admin. Only pem format file at present. The files can be updated dynamicly. 88 | usercert = "/etc/rmd/acl/roles/user/" # A cert is used to describe user info. These cert files in this path are used to define the user with low privilege. Only pem format file at present. The files can be updated dynamicly. 89 | 90 | [pam] 91 | service = "rmd" 92 | 93 | ` 94 | -------------------------------------------------------------------------------- /deb.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rmd", 3 | "maintainer": "intel-rmd ", 4 | "description": "Resource management daemon", 5 | "homepage": "https://github.com/intel/rmd", 6 | "files": [ 7 | { 8 | "from": "build/linux/x86_64/!name!", 9 | "to": "/usr/local/sbin/", 10 | "base": "build/!arch!", 11 | "fperm": "0755" 12 | }, 13 | { 14 | "from": "etc/**/**", 15 | "to": "/usr/local" 16 | } 17 | ], 18 | "copyrights": [ 19 | { 20 | "files": "*", 21 | "copyright": "2017 intel-rmd ", 22 | "license": "Apache-2.0", 23 | "file": "LICENSE" 24 | } 25 | ], 26 | "pre-depends": [ 27 | "openssl", 28 | "libpam0g-dev" 29 | ], 30 | "systemd-file": "packaging/rmd.service", 31 | "postinst-file": "packaging/postinst.sh", 32 | "postrm-file": "packaging/postrm.sh", 33 | "prerm-file": "packaging/prerm.sh" 34 | } 35 | -------------------------------------------------------------------------------- /docs/Prerequisite.md: -------------------------------------------------------------------------------- 1 | # Pre-requires 2 | 3 | ## Hardware 4 | 5 | RMD requires specific hardware support. It requires the host to have 6 | [Intel(R) Resource Director Technology][1] features, including below: 7 | 8 | [1]: https://www.intel.com/content/www/us/en/architecture-and-technology/resource-director-technology.html 9 | 10 | - Intel(R) Xeon(R) processor E5 v3 11 | - Intel(R) Xeon(R) processor D 12 | - Intel(R) Xeon(R) processor E3 v4 13 | - Intel(R) Xeon(R) processor E5 v4 14 | - Intel(R) Xeon(R) Scalable Processors (6) 15 | - Intel(R) Atom(R) processor for Server C3000 16 | 17 | ## Software 18 | 19 | RMD CAT & MBA handling module depends on linux **resctrl sysfs** and **PQOS library**. Minimal required PQOS library version is **4.1**. PQOS is initialized with OS driver support only - MSR is not supported. 20 | 21 | For *resctrl* support upstream linux kernel version should be higher than *4.10* for CAT and MBA in default mode. To use MBA in controller mode (Mbps configuration) at least kernel *4.18* is required. In case of linux distribution specific kernels please check distro documentation for *resctrl* and *MBA* support. 22 | 23 | To check if your host supports `resctrl` or not, check the out put of this 24 | command line: 25 | 26 | ``` 27 | cat /proc/filesystems | grep resctrl 28 | ``` 29 | 30 | Optional external *pstate* module requires *intel_pstate* or *acpi* CPU scaling driver to monitor and change the CPU cores frequencies. 31 | 32 | To check which driver is used on your host run following command in your Linux shell: 33 | 34 | ``` 35 | cat /sys/devices/system/cpu/cpufreq/policy0/scaling_driver 36 | ``` 37 | 38 | This module is a dynamically loadable external plugin and is delivered separately. -------------------------------------------------------------------------------- /docs/pic/Workload-DELETE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/rmd/233fd3d070060f90d0836b039bbe2171d4881b8d/docs/pic/Workload-DELETE.png -------------------------------------------------------------------------------- /docs/pic/Workload-POST.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/rmd/233fd3d070060f90d0836b039bbe2171d4881b8d/docs/pic/Workload-POST.png -------------------------------------------------------------------------------- /docs/pic/forking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/rmd/233fd3d070060f90d0836b039bbe2171d4881b8d/docs/pic/forking.png -------------------------------------------------------------------------------- /docs/pic/initialized-modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/rmd/233fd3d070060f90d0836b039bbe2171d4881b8d/docs/pic/initialized-modules.png -------------------------------------------------------------------------------- /docs/pic/module-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/rmd/233fd3d070060f90d0836b039bbe2171d4881b8d/docs/pic/module-flow.png -------------------------------------------------------------------------------- /docs/pic/rest-request-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/rmd/233fd3d070060f90d0836b039bbe2171d4881b8d/docs/pic/rest-request-flow.png -------------------------------------------------------------------------------- /docs/pic/rmd_logical_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/rmd/233fd3d070060f90d0836b039bbe2171d4881b8d/docs/pic/rmd_logical_view.png -------------------------------------------------------------------------------- /docs/pic/rmd_pools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/rmd/233fd3d070060f90d0836b039bbe2171d4881b8d/docs/pic/rmd_pools.png -------------------------------------------------------------------------------- /docs/reference/README.md: -------------------------------------------------------------------------------- 1 | # Reference Docs 2 | 3 | This directory contains some reference docs for RDT usage. 4 | 5 | ## CAT 6 | 7 | There are 2 ways to use [CAT feature][0]: 8 | 9 | ### 1. Associate logical thread with logical CLOSS 10 | a): Determine whether the CPU supports CAT via the CPUID instruction. As described in the Intel Software Developer’s Manual, CPUID leaf 0x10 provides detailed information on the capabilities of the CAT feature. 11 | b): Configure the class of service (CLOS) to define the amount of resources (cache space) available via MSRs. 12 | c): Associate each logical thread with an available logical CLOS. 13 | d): As the OS/VMM swaps a thread or VCPU onto a core, update the CLOS on the core via the IA32_PQR_ASSOC MSR, which ensures that the resource usage is controlled via the bitmasks configured in step b. 14 | 15 | In this way, we need OS/VMM enabling, this part of work has been done by Linux kernel 4.10. see `linux-kernel-user-interface.txt` for details. 16 | We can leverage this OS/VMM interface and no worry about CLOS -> hard core association. OS scheduler should in charge of migrate CLOS to core. 17 | [Linux kernel][1] provides /sys/fs/resctrl interface to operate on each cache resource. 18 | 19 | ### 2. Pin CLOS to hardware thread(CPU Core) 20 | 21 | `An alternate usage method is possible for non-enabled operating systems and VMMs where CLOS are pinned to hardware threads, then software threads are pinned to hardware threads; however OS/VMM enabling is recommended wherever possible in order to avoid the need to pin apps.` 22 | 23 | In this way, we need to pin our APP/VM threads on some specified cores, then associate CLOS to cores, this is what libpqos does now. 24 | See `libpqos-user-interface.txt` for how to [use CAT features][2]. 25 | 26 | [0]: https://software.intel.com/en-us/articles/cache-allocation-technology-usage-models "cat usage" 27 | [1]: https://chromium.googlesource.com/external/github.com/altera-opensource/linux-socfpga/+/refs/heads/master/Documentation/x86/intel_rdt_ui.txt "intel rdt ui" 28 | [2]: https://github.com/01org/intel-cmt-cat/tree/master/rdtset "intel-cmt-cat" 29 | -------------------------------------------------------------------------------- /etc/rmd/acl/roles/admin/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFVTCCAz2gAwIBAgIJAPrxrF2PBGlkMA0GCSqGSIb3DQEBCwUAMIGYMQswCQYD 3 | VQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UEBwwHQmVpSmluZzETMBEG 4 | A1UECgwKT3BlblNvdXJjZTEMMAoGA1UECwwDUk1EMSQwIgYDVQQDDBtSZXNvdXJj 5 | ZSBNYW5hZ2VtZW50IERhZWRtb24xHDAaBgkqhkiG9w0BCQEWDXJtZEBpbnRlbC5j 6 | b20wIBcNMTcwOTI0MjIxODUzWhgPMjExNzA4MzEyMjE4NTNaMCAxDjAMBgNVBAsM 7 | BUFkbWluMQ4wDAYDVQQDDAVBbGljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC 8 | AgoCggIBALRJmUeEHDY1MuyiSDBwTzVT80bZsgMaNGkyx5M5c3itvECeXDLXGtP7 9 | sksf0rR+GT9ckg9MuAijTk7OxtNTsfyU4COlrPzcH/9xRHwznkiYItYt0uQgXPkC 10 | 591aJ1gI+inzKjOUEIAyvlK2sclrlHyUDTga/YTsrmC+wC+IDm2wZIc0EdkN9wsi 11 | MH7EDbK1X3BsQ+bjeGaWDJGRvFbM3RV6frveuKqm15CyIIkXU8LLlvcXht1Oni+O 12 | CKa+UmAluEFsga5qa8LZ096/KoplOsU8fXGc0xxgPP5ft1cxxuwCekDBqMagpB3j 13 | SPtdkiEXHclj3qXGmJhB0G9XBZS3mLNBsSCJMZKNEaYB7n3c/ZCyMbRqVS1TU4z4 14 | vXHXqAWS6vNYmZChU6Zw6coXoz7XEZWdW0U51VHqVyBlb6vTpm4nO/pEfbpEPoDh 15 | f0AXZBRcA0H6qihEgnZPV9Kfo+nISEe2kECIWm0x5+KDIhEs0Yazf/XbLNgIqOik 16 | 4pG6y7hIWJWhOh6C+OoWlqDs/nf4q7wRN5HpcmZvQ1QT7AHHyIHMDF6dx6PqyUmN 17 | zHu8brnIefA9jJ2AjLg65pzKNw/msQqxZ2JzhCET40fUrwcCmMogqDUPBWs/R9fa 18 | Q0nut1fgWjwMuKL+HH0CfeTBUgqG9Igk76anNdSWhUg4l2zl4bDVAgMBAAGjFzAV 19 | MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4ICAQAcTlQPNvz5 20 | ihrGtbpb1KhW0Q95mpmtB6OIhUghoAYIGDiFG8ROCJ2eTuxXVdsAquI+UK7uOXdL 21 | n30q5vN+MX9bZzJcnCWPwjVefrZdVi/e0wz1sd3z5I0yttl2/j1pBglmyVUQsI5U 22 | h0NRbG9hxx/FQLBIddW8WEh+i+zSvru0J+X3YNgzZlvuj6gvdt2KA1qBpIRzEg9n 23 | SdjzofcMz24OLfOU15b3j4pvJqP4c/BWa45ZJwBdZCVbMoMC7NpoerthrOj/NGHF 24 | 6VKhY054oEpgHsnbwGSPmdUFhIIjoM+m1qmPfHAXz/PqpEvF98/x3LVqc69zwmep 25 | wU4MPBNnRWCvoxxlRlM5CKBR/na1DlGGFld488of9gAyM/JNo5euEaAhlaucCvg+ 26 | wD5Tz8ZbQbtU4f0Tryyx443+gEij6cPGYv4Vq+/YMk5buHy85WRVOPvjlJSXX/kf 27 | AZhA33fp58dHmVuWgthNrk7EC6KB1UX5jdMaI4TRR/U+LEjyqEKksriee0o9Y2YR 28 | hiwQ/Y6qynr6J/0TVVRaYVT4ZyfdtgtvgWL1tvlzpybgnFZoH4+85+U/KKT7kwhU 29 | 6TJDqmjict02uDUB6psk/EAvoztJx3Sz0I+KC1tHbCo6c5p3FU8tpg6tZw8cocHJ 30 | qEP+e77A4h/FUMyinaiKrrupN0cxDkHaSA== 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /etc/rmd/acl/roles/user/user-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFUjCCAzqgAwIBAgIJAKb976c/bwRiMA0GCSqGSIb3DQEBCwUAMIGYMQswCQYD 3 | VQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UEBwwHQmVpSmluZzETMBEG 4 | A1UECgwKT3BlblNvdXJjZTEMMAoGA1UECwwDUk1EMSQwIgYDVQQDDBtSZXNvdXJj 5 | ZSBNYW5hZ2VtZW50IERhZWRtb24xHDAaBgkqhkiG9w0BCQEWDXJtZEBpbnRlbC5j 6 | b20wIBcNMTcxMDE2MjI1NDAwWhgPMjExNzA5MjIyMjU0MDBaMB0xDDAKBgNVBAMM 7 | A0VsaTENMAsGA1UECwwEVXNlcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC 8 | ggIBAMwzCfqaxt6UmYR5rkZBeJrlHXKF+AhtGINCjHiQuSYAT4UaeFrZRAujOFsP 9 | 5kHjdKHamuLulgzlAkYhQPvehfjNb7ByRUoc99FD7fe/fmoZjjrUMbzBYFg2DSCp 10 | 6hBZSdX+qA3V6dKgcRkxjQkAF+Pxt1dsPLneQqKUmWnkwL1G3RA+vzYAZFLb6FSF 11 | CcQSGSRy0ivP32DcCAgIKup6BNONgYtG7fbAcDF/vlM8A2jCDfterS4He8mqxCal 12 | 9f6tP6JsYVOeMA3iDWkvpKkCmqYdnjYT3vwLstHFbIvW92FtWdeWHZcgIrNfY3Za 13 | dMLpUI0x3Up0ABJNZR21x4frxwcaacQTP2Fc1XfijIQwGC/Z2lZ/JPui6ZdTc0Vx 14 | Utd0vDaaA0ANEgliju7oe+P050bRC2FWSv+sDJb+N4/bPdCQHNSV/G3eb1NPiVl9 15 | Z8t1YtpmLiGmBZKRI09xXlusPwzmF3P5z4iqP0u77s0jWr4oEcvSgz88EDi4m0pP 16 | KDf7bOeTZuaRVRCu9QuxORab8YvmQ20EnUM+R0UWM+LaMYrabpnuGI6SazrqE33i 17 | TWC8mZbBNI+nNm/4H1yJSOMCMOvImm9bcXt6JYTCDPAAchZ9x0m+K3e4mOWUrBei 18 | rVZeTMuNTmGX27SfDWesNQvlIfrUE3CHXCyDQswGDQqcppvjAgMBAAGjFzAVMBMG 19 | A1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4ICAQCqqShDd6Mjx//D 20 | Y4qvI0okI/HUCV6CQ5yboA8pSROljCDD0cme3nllHjNKXcKO83LbTQ9eya2u3M/P 21 | wksku3R24rjjeShMMcG5w1ef0DE04JVrGkMX8NVi8dJZBvU29S2T8v1QyAxOSRtF 22 | Aq/8Zt21JI5IvBltz4VzLEQiS8GbJ3Z0JpS4YriTdEAmdUf0jbPz6+YC7hMIKDuz 23 | MCWPJF4d/M2OH6HMqSSAIxdsMZPtbT7wNF9IKf+A5axarrOBycWTO9QcA7GRMP08 24 | JDDgE0nCNCsRs9Mzzm8oS0QyvCG53ONwwdnWQR5UPcgu9LECfuK9theecjs0/3QZ 25 | 4qPER9xUeK1knrKmjke6ULgm/07nwkmZGX2EjBJbXzunAiJA+JNEP7uCYTjVYfEd 26 | lkj7MWMdHfleOolUBBVFDBLQ0QkCHhMcnja8JP/IW6e7t4ePcpaQ5bR1EispPDsi 27 | IREI1MADTs90GOLCCTYYg8h4E6nUfhJJTRSqw5ewnjCqRrbDyxRsfN3+BIJtBHqc 28 | Kcihv01WHKio5GYWC92CwIgzkZ8kVYZQL1X496IU5d4Fb8Mk0/Cw9x+WoDCtADRb 29 | qkYkOg2qrFq1zQ90bylzB/CWduJ/wKQuj+Ui5YUnB7uP0ZwTM5bdMMvvQ5hkb739 30 | 8EbDhg6IPg4zsp6bP4VZrtcW+A4Rkw== 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /etc/rmd/acl/url/model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | 4 | [policy_definition] 5 | p = sub, obj, act 6 | 7 | 8 | [role_definition] 9 | g = _, _ 10 | 11 | [policy_effect] 12 | e = some(where (p.eft == allow)) 13 | 14 | [matchers] 15 | m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act) 16 | -------------------------------------------------------------------------------- /etc/rmd/acl/url/policy.csv: -------------------------------------------------------------------------------- 1 | p, user, /cache/l*/*, GET 2 | p, user, /cache/l*, GET 3 | p, user, /cache, GET 4 | p, user, /policy, GET 5 | p, user, /workloads, GET 6 | p, user, /workloads/*, GET 7 | p, user, /hospitality, GET 8 | 9 | p, root, /workloads, POST 10 | p, root, /workloads/*, (PATCH)|(DELETE) 11 | 12 | g, root, user 13 | g, admin, root 14 | -------------------------------------------------------------------------------- /etc/rmd/cert/client/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGBzCCA++gAwIBAgIJAKOif0HLFu3+MA0GCSqGSIb3DQEBCwUAMIGYMQswCQYD 3 | VQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UEBwwHQmVpSmluZzETMBEG 4 | A1UECgwKT3BlblNvdXJjZTEMMAoGA1UECwwDUk1EMSQwIgYDVQQDDBtSZXNvdXJj 5 | ZSBNYW5hZ2VtZW50IERhZWRtb24xHDAaBgkqhkiG9w0BCQEWDXJtZEBpbnRlbC5j 6 | b20wIBcNMTcwOTI0MjIxODUxWhgPMjExNzA4MzEyMjE4NTFaMIGYMQswCQYDVQQG 7 | EwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UEBwwHQmVpSmluZzETMBEGA1UE 8 | CgwKT3BlblNvdXJjZTEMMAoGA1UECwwDUk1EMSQwIgYDVQQDDBtSZXNvdXJjZSBN 9 | YW5hZ2VtZW50IERhZWRtb24xHDAaBgkqhkiG9w0BCQEWDXJtZEBpbnRlbC5jb20w 10 | ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC18ZPojAv65nRuCBO24O+q 11 | eE9B1MikHhKWMahc2xs5h/INJGn9VLH9dmxd2dFF04rPO6cMabesBJkCYcbgLBli 12 | uoJZSUpYQCquQZ2WT8gh91h+noVYFU73hS/IW7/1ZesKV221DQ/a3Sp8QThicpHo 13 | jut5QuZ3BCscgI2ZxJwdVvM7G0nFvcsU5RDGBXAJ5yqIzxncAT2xzvBWyqmS7kwH 14 | Uf+lbMfYL32t0DOBqkY/vgf+zccGCwcXdHWp+6OLTMFiVkf3dOAJqg8ebfUwmw1r 15 | pwUbP1rSDqpYtUJB04owS9aWMljavDoZL0WiXsiMOd/BSEKCr7Z514EdWSgnXcXN 16 | HJmOUMY6UOBtwZ1MOTf1OKPnsy8ViuAGUNVJlJJN0coDQ0nIXWTijcV7WFAr3Flj 17 | m/3pPhdZq5ZWncsv3vsEy174D2X23DFtNTU23rfhyEerVZ+ZUTOgJe15eeq5rmwJ 18 | Mrr4Pe4917LtbYGhAuo3kNPxaH098S77ttMnZA0BGRtHbQZIKq4kT4hP4lMm/es6 19 | AUmXR+uRY+Me91vOTG6Tio8ML2uEs4r8BBa7n9+800volzFSD7E6dVh+1ORJuhZ/ 20 | IUD0FpbOvp38l4AU65A5IMF3Ey4SL7rt5qmupXHcXsO5ek0R+EJqKz6kgicCRfhJ 21 | +iTHR6Gad3NQ4ewS0QkmbQIDAQABo1AwTjAdBgNVHQ4EFgQUPgX6hbu3vm+pTg/R 22 | O8z7+V9RQW0wHwYDVR0jBBgwFoAUPgX6hbu3vm+pTg/RO8z7+V9RQW0wDAYDVR0T 23 | BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAASapNG/+/lq4yC4j4WdKUEGx+c0H 24 | zEOeKZsUFBnLa7kiEWENcBX7U4h0BClRIZx3sweRUBCMfvHfn+mBdqNQR4OMyk4A 25 | tT3Vik6ldHiyJ0DRnfm6kW3pzLZVxUoI7fjOZvpAqKphhrp9mSKAG9GNjAwGBHu2 26 | FBalVEA3w/NezBZxvZYjfjSEbMXGJtLZ8PdjGs2JR81C6LQZVn3riwpjK/lWkVxc 27 | cF87mq1k0nlHoVicKhqEKN5dpNrTp5uwx3E+lLHfFs3oMEaRY2D0WrEniwL0CcPo 28 | 2FkOqcC3Ap/BzmFg/sgTDQBY1zWwtR5BCPjzaK+vZTBNsRkV55+On69K2WQ37seT 29 | PL1lNkQdP04sBJ6pz7d4T/JaPXfmvS4p2SyuQOY2L6/LmNF+g6WgWPRUdIfbbryH 30 | oFgEITwTjy5txSsf6G/tH1Q9TxEENzZPrF0L4EHZdIRAY5S09OqrDjey7Y+d0lEh 31 | wBi9qMaXgYZjHH/zp8AFRVPUCNGxpaG6YRLh4aSpxjtr6DJkIeh5H0Zlq/xXnoPw 32 | ivMXKB5f/jToUmcDLBik3qDnWmDJWlnBG7oUBKOFjOaMVd/DLBaGaNDqyJKw90Ps 33 | PF/nsIyHGo1rD/nHIXgrLSt7pWAry9VhmI6Auf5DOXrGMa6L+YmTXeuNREAO9r/R 34 | ILaQbwfjddwW/Hc= 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /etc/rmd/cert/client/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFVTCCAz2gAwIBAgIJAPrxrF2PBGlkMA0GCSqGSIb3DQEBCwUAMIGYMQswCQYD 3 | VQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UEBwwHQmVpSmluZzETMBEG 4 | A1UECgwKT3BlblNvdXJjZTEMMAoGA1UECwwDUk1EMSQwIgYDVQQDDBtSZXNvdXJj 5 | ZSBNYW5hZ2VtZW50IERhZWRtb24xHDAaBgkqhkiG9w0BCQEWDXJtZEBpbnRlbC5j 6 | b20wIBcNMTcwOTI0MjIxODUzWhgPMjExNzA4MzEyMjE4NTNaMCAxDjAMBgNVBAsM 7 | BUFkbWluMQ4wDAYDVQQDDAVBbGljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC 8 | AgoCggIBALRJmUeEHDY1MuyiSDBwTzVT80bZsgMaNGkyx5M5c3itvECeXDLXGtP7 9 | sksf0rR+GT9ckg9MuAijTk7OxtNTsfyU4COlrPzcH/9xRHwznkiYItYt0uQgXPkC 10 | 591aJ1gI+inzKjOUEIAyvlK2sclrlHyUDTga/YTsrmC+wC+IDm2wZIc0EdkN9wsi 11 | MH7EDbK1X3BsQ+bjeGaWDJGRvFbM3RV6frveuKqm15CyIIkXU8LLlvcXht1Oni+O 12 | CKa+UmAluEFsga5qa8LZ096/KoplOsU8fXGc0xxgPP5ft1cxxuwCekDBqMagpB3j 13 | SPtdkiEXHclj3qXGmJhB0G9XBZS3mLNBsSCJMZKNEaYB7n3c/ZCyMbRqVS1TU4z4 14 | vXHXqAWS6vNYmZChU6Zw6coXoz7XEZWdW0U51VHqVyBlb6vTpm4nO/pEfbpEPoDh 15 | f0AXZBRcA0H6qihEgnZPV9Kfo+nISEe2kECIWm0x5+KDIhEs0Yazf/XbLNgIqOik 16 | 4pG6y7hIWJWhOh6C+OoWlqDs/nf4q7wRN5HpcmZvQ1QT7AHHyIHMDF6dx6PqyUmN 17 | zHu8brnIefA9jJ2AjLg65pzKNw/msQqxZ2JzhCET40fUrwcCmMogqDUPBWs/R9fa 18 | Q0nut1fgWjwMuKL+HH0CfeTBUgqG9Igk76anNdSWhUg4l2zl4bDVAgMBAAGjFzAV 19 | MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4ICAQAcTlQPNvz5 20 | ihrGtbpb1KhW0Q95mpmtB6OIhUghoAYIGDiFG8ROCJ2eTuxXVdsAquI+UK7uOXdL 21 | n30q5vN+MX9bZzJcnCWPwjVefrZdVi/e0wz1sd3z5I0yttl2/j1pBglmyVUQsI5U 22 | h0NRbG9hxx/FQLBIddW8WEh+i+zSvru0J+X3YNgzZlvuj6gvdt2KA1qBpIRzEg9n 23 | SdjzofcMz24OLfOU15b3j4pvJqP4c/BWa45ZJwBdZCVbMoMC7NpoerthrOj/NGHF 24 | 6VKhY054oEpgHsnbwGSPmdUFhIIjoM+m1qmPfHAXz/PqpEvF98/x3LVqc69zwmep 25 | wU4MPBNnRWCvoxxlRlM5CKBR/na1DlGGFld488of9gAyM/JNo5euEaAhlaucCvg+ 26 | wD5Tz8ZbQbtU4f0Tryyx443+gEij6cPGYv4Vq+/YMk5buHy85WRVOPvjlJSXX/kf 27 | AZhA33fp58dHmVuWgthNrk7EC6KB1UX5jdMaI4TRR/U+LEjyqEKksriee0o9Y2YR 28 | hiwQ/Y6qynr6J/0TVVRaYVT4ZyfdtgtvgWL1tvlzpybgnFZoH4+85+U/KKT7kwhU 29 | 6TJDqmjict02uDUB6psk/EAvoztJx3Sz0I+KC1tHbCo6c5p3FU8tpg6tZw8cocHJ 30 | qEP+e77A4h/FUMyinaiKrrupN0cxDkHaSA== 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /etc/rmd/cert/client/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /etc/rmd/cert/client/user-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFUjCCAzqgAwIBAgIJAKb976c/bwRiMA0GCSqGSIb3DQEBCwUAMIGYMQswCQYD 3 | VQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UEBwwHQmVpSmluZzETMBEG 4 | A1UECgwKT3BlblNvdXJjZTEMMAoGA1UECwwDUk1EMSQwIgYDVQQDDBtSZXNvdXJj 5 | ZSBNYW5hZ2VtZW50IERhZWRtb24xHDAaBgkqhkiG9w0BCQEWDXJtZEBpbnRlbC5j 6 | b20wIBcNMTcxMDE2MjI1NDAwWhgPMjExNzA5MjIyMjU0MDBaMB0xDDAKBgNVBAMM 7 | A0VsaTENMAsGA1UECwwEVXNlcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC 8 | ggIBAMwzCfqaxt6UmYR5rkZBeJrlHXKF+AhtGINCjHiQuSYAT4UaeFrZRAujOFsP 9 | 5kHjdKHamuLulgzlAkYhQPvehfjNb7ByRUoc99FD7fe/fmoZjjrUMbzBYFg2DSCp 10 | 6hBZSdX+qA3V6dKgcRkxjQkAF+Pxt1dsPLneQqKUmWnkwL1G3RA+vzYAZFLb6FSF 11 | CcQSGSRy0ivP32DcCAgIKup6BNONgYtG7fbAcDF/vlM8A2jCDfterS4He8mqxCal 12 | 9f6tP6JsYVOeMA3iDWkvpKkCmqYdnjYT3vwLstHFbIvW92FtWdeWHZcgIrNfY3Za 13 | dMLpUI0x3Up0ABJNZR21x4frxwcaacQTP2Fc1XfijIQwGC/Z2lZ/JPui6ZdTc0Vx 14 | Utd0vDaaA0ANEgliju7oe+P050bRC2FWSv+sDJb+N4/bPdCQHNSV/G3eb1NPiVl9 15 | Z8t1YtpmLiGmBZKRI09xXlusPwzmF3P5z4iqP0u77s0jWr4oEcvSgz88EDi4m0pP 16 | KDf7bOeTZuaRVRCu9QuxORab8YvmQ20EnUM+R0UWM+LaMYrabpnuGI6SazrqE33i 17 | TWC8mZbBNI+nNm/4H1yJSOMCMOvImm9bcXt6JYTCDPAAchZ9x0m+K3e4mOWUrBei 18 | rVZeTMuNTmGX27SfDWesNQvlIfrUE3CHXCyDQswGDQqcppvjAgMBAAGjFzAVMBMG 19 | A1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4ICAQCqqShDd6Mjx//D 20 | Y4qvI0okI/HUCV6CQ5yboA8pSROljCDD0cme3nllHjNKXcKO83LbTQ9eya2u3M/P 21 | wksku3R24rjjeShMMcG5w1ef0DE04JVrGkMX8NVi8dJZBvU29S2T8v1QyAxOSRtF 22 | Aq/8Zt21JI5IvBltz4VzLEQiS8GbJ3Z0JpS4YriTdEAmdUf0jbPz6+YC7hMIKDuz 23 | MCWPJF4d/M2OH6HMqSSAIxdsMZPtbT7wNF9IKf+A5axarrOBycWTO9QcA7GRMP08 24 | JDDgE0nCNCsRs9Mzzm8oS0QyvCG53ONwwdnWQR5UPcgu9LECfuK9theecjs0/3QZ 25 | 4qPER9xUeK1knrKmjke6ULgm/07nwkmZGX2EjBJbXzunAiJA+JNEP7uCYTjVYfEd 26 | lkj7MWMdHfleOolUBBVFDBLQ0QkCHhMcnja8JP/IW6e7t4ePcpaQ5bR1EispPDsi 27 | IREI1MADTs90GOLCCTYYg8h4E6nUfhJJTRSqw5ewnjCqRrbDyxRsfN3+BIJtBHqc 28 | Kcihv01WHKio5GYWC92CwIgzkZ8kVYZQL1X496IU5d4Fb8Mk0/Cw9x+WoDCtADRb 29 | qkYkOg2qrFq1zQ90bylzB/CWduJ/wKQuj+Ui5YUnB7uP0ZwTM5bdMMvvQ5hkb739 30 | 8EbDhg6IPg4zsp6bP4VZrtcW+A4Rkw== 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /etc/rmd/cert/client/user-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /etc/rmd/cert/server/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGBzCCA++gAwIBAgIJAKOif0HLFu3+MA0GCSqGSIb3DQEBCwUAMIGYMQswCQYD 3 | VQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UEBwwHQmVpSmluZzETMBEG 4 | A1UECgwKT3BlblNvdXJjZTEMMAoGA1UECwwDUk1EMSQwIgYDVQQDDBtSZXNvdXJj 5 | ZSBNYW5hZ2VtZW50IERhZWRtb24xHDAaBgkqhkiG9w0BCQEWDXJtZEBpbnRlbC5j 6 | b20wIBcNMTcwOTI0MjIxODUxWhgPMjExNzA4MzEyMjE4NTFaMIGYMQswCQYDVQQG 7 | EwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UEBwwHQmVpSmluZzETMBEGA1UE 8 | CgwKT3BlblNvdXJjZTEMMAoGA1UECwwDUk1EMSQwIgYDVQQDDBtSZXNvdXJjZSBN 9 | YW5hZ2VtZW50IERhZWRtb24xHDAaBgkqhkiG9w0BCQEWDXJtZEBpbnRlbC5jb20w 10 | ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC18ZPojAv65nRuCBO24O+q 11 | eE9B1MikHhKWMahc2xs5h/INJGn9VLH9dmxd2dFF04rPO6cMabesBJkCYcbgLBli 12 | uoJZSUpYQCquQZ2WT8gh91h+noVYFU73hS/IW7/1ZesKV221DQ/a3Sp8QThicpHo 13 | jut5QuZ3BCscgI2ZxJwdVvM7G0nFvcsU5RDGBXAJ5yqIzxncAT2xzvBWyqmS7kwH 14 | Uf+lbMfYL32t0DOBqkY/vgf+zccGCwcXdHWp+6OLTMFiVkf3dOAJqg8ebfUwmw1r 15 | pwUbP1rSDqpYtUJB04owS9aWMljavDoZL0WiXsiMOd/BSEKCr7Z514EdWSgnXcXN 16 | HJmOUMY6UOBtwZ1MOTf1OKPnsy8ViuAGUNVJlJJN0coDQ0nIXWTijcV7WFAr3Flj 17 | m/3pPhdZq5ZWncsv3vsEy174D2X23DFtNTU23rfhyEerVZ+ZUTOgJe15eeq5rmwJ 18 | Mrr4Pe4917LtbYGhAuo3kNPxaH098S77ttMnZA0BGRtHbQZIKq4kT4hP4lMm/es6 19 | AUmXR+uRY+Me91vOTG6Tio8ML2uEs4r8BBa7n9+800volzFSD7E6dVh+1ORJuhZ/ 20 | IUD0FpbOvp38l4AU65A5IMF3Ey4SL7rt5qmupXHcXsO5ek0R+EJqKz6kgicCRfhJ 21 | +iTHR6Gad3NQ4ewS0QkmbQIDAQABo1AwTjAdBgNVHQ4EFgQUPgX6hbu3vm+pTg/R 22 | O8z7+V9RQW0wHwYDVR0jBBgwFoAUPgX6hbu3vm+pTg/RO8z7+V9RQW0wDAYDVR0T 23 | BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAASapNG/+/lq4yC4j4WdKUEGx+c0H 24 | zEOeKZsUFBnLa7kiEWENcBX7U4h0BClRIZx3sweRUBCMfvHfn+mBdqNQR4OMyk4A 25 | tT3Vik6ldHiyJ0DRnfm6kW3pzLZVxUoI7fjOZvpAqKphhrp9mSKAG9GNjAwGBHu2 26 | FBalVEA3w/NezBZxvZYjfjSEbMXGJtLZ8PdjGs2JR81C6LQZVn3riwpjK/lWkVxc 27 | cF87mq1k0nlHoVicKhqEKN5dpNrTp5uwx3E+lLHfFs3oMEaRY2D0WrEniwL0CcPo 28 | 2FkOqcC3Ap/BzmFg/sgTDQBY1zWwtR5BCPjzaK+vZTBNsRkV55+On69K2WQ37seT 29 | PL1lNkQdP04sBJ6pz7d4T/JaPXfmvS4p2SyuQOY2L6/LmNF+g6WgWPRUdIfbbryH 30 | oFgEITwTjy5txSsf6G/tH1Q9TxEENzZPrF0L4EHZdIRAY5S09OqrDjey7Y+d0lEh 31 | wBi9qMaXgYZjHH/zp8AFRVPUCNGxpaG6YRLh4aSpxjtr6DJkIeh5H0Zlq/xXnoPw 32 | ivMXKB5f/jToUmcDLBik3qDnWmDJWlnBG7oUBKOFjOaMVd/DLBaGaNDqyJKw90Ps 33 | PF/nsIyHGo1rD/nHIXgrLSt7pWAry9VhmI6Auf5DOXrGMa6L+YmTXeuNREAO9r/R 34 | ILaQbwfjddwW/Hc= 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /etc/rmd/cert/server/rmd-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFazCCA1OgAwIBAgIJAPrxrF2PBGljMA0GCSqGSIb3DQEBCwUAMIGYMQswCQYD 3 | VQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UEBwwHQmVpSmluZzETMBEG 4 | A1UECgwKT3BlblNvdXJjZTEMMAoGA1UECwwDUk1EMSQwIgYDVQQDDBtSZXNvdXJj 5 | ZSBNYW5hZ2VtZW50IERhZWRtb24xHDAaBgkqhkiG9w0BCQEWDXJtZEBpbnRlbC5j 6 | b20wIBcNMTcwOTI0MjIxODUxWhgPMjExNzA4MzEyMjE4NTFaMBUxEzARBgNVBAMM 7 | ClJNRC1TRVJWRVIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJebMf 8 | kl3ky1fbE8SpsPwtEs7wseqDOb0BG4eo1/q2AFs0y2T4MB9P6EbK38DoFrtmmV+C 9 | cxlqQ52J4+OMgD50RPvDpD1G4obGqUMM+659dgSzEuF8dAkPB34OjQetdGB+1Jyd 10 | 2bsgfiGHP25IpIwal1opawXZCTZeieRkXvqUm460yjK11RyWdn+MdfO8QN/RvJRT 11 | wGwCpmZYmw8dj39HUmyVGiMkS/MCfWmxJ7KcaWIzReeGe6Njezo545tCw3JE7hAj 12 | wZabxuYfOFPv0bZiDFmEe+V8+tK7kfRUPL4slsPjBQz8yDE2tt5JMOAfhMZOSMsY 13 | 0fPGndfhpQElRne73J0L2IPGu3CSdNB/x775jMxlkS+Qqs8EJtKbD6vlhqpoZEl5 14 | 2Iktd0/evYcQXjTcT7JKQCZjenIadxOV5eRQoWeDSYK/7X+6FHAfKbVF9Box0C30 15 | hxv2lyE3M/63c0sQPmnrU7kNF64J145B3VAPUCLIqhteEKK/BmcqlRwwLNgbOk1W 16 | VY7Gg0N6sSfZYngZAPzhwrFe/PPpX4FlJpN44ZjQ8q2MG1fMhqYfOb8Wrwa7yTjT 17 | MixU2Vf4aYcGezwJ2APjAw8hin+RFhFeb9DqcQDeg9QxT7Yqx/tSQd6xWg3Ik+pf 18 | dtuDhCwZ81OBmwoWBF78TX2aGE6Tcjlzgfu04wIDAQABozgwNjAfBgNVHREEGDAW 19 | gg5ybWQtbmFtZXNlcnZlcocEfwAAATATBgNVHSUEDDAKBggrBgEFBQcDATANBgkq 20 | hkiG9w0BAQsFAAOCAgEAM0GIGnDi/rfhIgbpOWVh3xTDhXct9eN4PqjMjnJhvelN 21 | OASQDYSoKobbrwtXmOjK6rb2e7OkhzQd80blh1IUv6tV260vU1HeE/yO9YzB0vLS 22 | qTnL6nMnOEBBlgEaieQezjLCrPTFPZ34GQIc3LFYVqMECiTaiUSD/pQcNqnyn/Sm 23 | mNr/89GHkHYj0lFBkzeJOKch+92v6JTCZRU0it/noyIlmqrA1NzFY3ICWhyMV321 24 | y9dmNBlG92lM7YzTX7JBWDAkcO7Na+EH/ZlxifaGaGv6MmtuVAGnoMIiQukZTtyF 25 | 6biaAgRTfBfTtpkMzsSjX/Mzv0/RktCBc31Bq4NaOwtQRpXJGlRg7psanrUextlN 26 | trYi2GEMIurvegbykpeCzArivLG8j4TmbfchEqaPUVVmqSDypVDF00lUhNDdKYtj 27 | GgjguPSl+ij0O6uJPgv+mO0VMnCz4M+mZ0cIEtWN67Wgv5raq4n3BmcKbk6hFtVM 28 | MiWy1ZO6Y7+EvtULEbVN7I92/W1SsZnCchJJFeGBAYVfyqDYX/B2r+vEYpj0csnT 29 | t1R4Tv6tKyELRqlx9Bq9vH32iZk4AcrNpppgIROjJyYsEYx5jfA8ga3Q6WFm7T5b 30 | yBbl4tsQy3Dr8PXLZeOypdwAHxeSICmel4GvwAutJCdhYDruJqSaIHBbrTdc7lE= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /etc/rmd/cert/server/rmd-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /etc/rmd/cpu_map.toml: -------------------------------------------------------------------------------- 1 | # This is a cpu microarch config. 2 | 3 | title = "cpu microarch" 4 | 5 | [[Broadwell]] 6 | family=6 7 | model=61 8 | 9 | [[Broadwell]] 10 | family=6 11 | model=79 12 | -------------------------------------------------------------------------------- /etc/rmd/pam/rmd: -------------------------------------------------------------------------------- 1 | auth sufficient pam_unix.so 2 | auth requisite pam_userdb.so db=/etc/rmd/pam/rmd_users crypt=crypt 3 | -------------------------------------------------------------------------------- /etc/rmd/pam/test/rmd: -------------------------------------------------------------------------------- 1 | auth sufficient pam_unix.so 2 | auth requisite pam_userdb.so db=/tmp/rmd_users crypt=crypt -------------------------------------------------------------------------------- /etc/rmd/policy.toml: -------------------------------------------------------------------------------- 1 | [broadwell] 2 | [broadwell.gold] 3 | [broadwell.gold.cache] 4 | max = 2 5 | min = 2 6 | [broadwell.silver] 7 | [broadwell.silver.cache] 8 | max = 2 9 | min = 1 10 | [broadwell.bronze] 11 | [broadwell.bronze.cache] 12 | max = 0 13 | min = 0 14 | [skylake] 15 | [skylake.gold] 16 | [skylake.gold.cache] 17 | max = 4 18 | min = 4 19 | [skylake.silver] 20 | [skylake.silver.cache] 21 | max = 3 22 | min = 1 23 | [skylake.bronze] 24 | [skylake.bronze.cache] 25 | max = 0 26 | min = 0 27 | -------------------------------------------------------------------------------- /etc/rmd/policy.yaml: -------------------------------------------------------------------------------- 1 | broadwell: 2 | gold: 3 | cache: 4 | max: 2 5 | min: 2 6 | silver: 7 | cache: 8 | max: 2 9 | Min: 1 10 | bronze: 11 | cache: 12 | max: 0 13 | Min: 0 14 | skylake: 15 | gold: 16 | cache: 17 | max: 4 18 | min: 4 19 | silver: 20 | cache: 21 | max: 3 22 | min: 1 23 | bronze: 24 | cache: 25 | max: 0 26 | Min: 0 27 | -------------------------------------------------------------------------------- /etc/rmd/rmd.toml: -------------------------------------------------------------------------------- 1 | # RMD configuration 2 | 3 | title = "RMD config" 4 | 5 | [default] 6 | # address = "localhost" 7 | # policypath = "/user/local/etc/rmd/policy.toml" 8 | # sysresctrl = "/sys/fs/resctrl" 9 | # tlsport = 8443 # requires a port number higher than 1024 10 | # certpath = "/etc/rmd/cert/server" # Only support pem format, hard code that CAFile is ca.pem, CertFile is rmd-cert.pem, KeyFile is rmd-key.pem 11 | # clientcapath = "/etc/rmd/cert/client" # Only support pem format, hard code that CAFile is ca.pem 12 | # clientauth = "challenge" # can be "no, require, require_any, challenge_given, challenge", challenge means require and verify. 13 | # unixsock = "/var/run/rmd.sock" 14 | # sysresctrl = "/sys/fs/resctrl" 15 | # plugins = "" # comma separated list of enabled RMD plugins, for each plugin (ex. PLUGINX) appropriate config section (ex. [PLUGINX]) is needed 16 | # openstackenable = false # OpenStack integration activation, please read UserGuide for more information 17 | # dbValidatorInterval = 30 # interval for database validator that checks if process for created workload is still running 18 | 19 | [rdt] 20 | # mbaMode = "percentage" # MBA mode of operation, possible options are: "none", "percentage" (used by default) and "mbps" 21 | 22 | [debug] 23 | # enabled = false # allow rmd to run without any auth with http protocol 24 | # debugport = 8081 25 | 26 | [log] 27 | # path = "/var/log/rmd/rmd.log" 28 | # env = "dev" # production or dev 29 | # level = "debug" 30 | # stdout = true 31 | 32 | [database] 33 | # backend = "bolt" 34 | # transport = "/var/run/rmd/rmd.db" 35 | # dbname = "rmd" 36 | 37 | [OSGroup] # mandatory 38 | # cacheways = 1 39 | # cpuset = "0" 40 | 41 | [InfraGroup] # optional 42 | # cacheways = 19 43 | # cpuset = "2-3" 44 | # arrary or comma-separated values 45 | # tasks = ["ovs*"] # just support Wildcards 46 | 47 | [CachePool] # Cache Pool config is optional 48 | # shrink = false # whether allow to shrink cache ways in best effort pool 49 | # max_allowed_shared = 10 # max allowed workload in shared pool, default is 10 50 | # guarantee = 10 51 | # besteffort = 7 52 | # shared = 2 53 | 54 | [acl] 55 | # path = "/etc/rmd/acl/"# 56 | # use CSV format 57 | # filter = "url" # at present just support "url", will support "ip, proto" 58 | # authorization = "role" # authorize the client, can identify client by signature, role(OU) or username(CN). Default value is signature. If value is signature, admincert and usercert should be set. 59 | # admincert = "/etc/rmd/acl/roles/admin/" # A cert is used to describe user info. These cert files in this path are used to define the users that are admin. Only pem format file at present. The files can be updated dynamically. 60 | # usercert = "/etc/rmd/acl/roles/user/" # A cert is used to describe user info. These cert files in this path are used to define the user with low privilege. Only pem format file at present. The files can be updated dynamically 61 | 62 | [pam] 63 | # service = "rmd" 64 | 65 | [openstack] 66 | # Path below is optional. If not given then file will not be generated 67 | providerConfigPath = "/etc/nova/provider_config/rmd.yaml" 68 | amqpuri = "amqp://user:password@127.0.0.1:5672/" # URI to openstack amqp server, required to subscribe to nova notifications, update with proper IP 69 | bindingKey = "versioned_notifications.info" 70 | keystoneUrl = "http://127.0.0.1/identity/v3/auth/tokens" # URI to Keystone server, required to get token, update with proper IP 71 | keystoneLogin = "login" 72 | keystonePassword = "password" 73 | -------------------------------------------------------------------------------- /external/sampleplugin/Makefile: -------------------------------------------------------------------------------- 1 | # This is Makefile for building RMD plugin as loadable .so file 2 | BUILD_DIR ?= ./build 3 | PLUGIN_SO_NAME ?= sampleplugin.so 4 | INSTALL_PATH ?= /etc/rmd/plugins 5 | 6 | .PHONY: all clean $(BUILD_DIR)/$(PLUGIN_SO_NAME) install 7 | 8 | all: $(BUILD_DIR)/$(PLUGIN_SO_NAME) 9 | 10 | clean: 11 | @rm -rf $(BUILD_DIR) 12 | 13 | $(BUILD_DIR)/$(PLUGIN_SO_NAME): $(BUILD_DIR) 14 | go build -buildmode=plugin -o $(BUILD_DIR)/$(PLUGIN_SO_NAME) ./ 15 | 16 | $(BUILD_DIR): 17 | @mkdir -p $(BUILD_DIR) 18 | 19 | install: 20 | @mkdir -p $(INSTALL_PATH) 21 | cp $(BUILD_DIR)/$(PLUGIN_SO_NAME) $(INSTALL_PATH)/ 22 | 23 | test: 24 | go test -v ./ 25 | -------------------------------------------------------------------------------- /external/sampleplugin/SamplePluginUG.md: -------------------------------------------------------------------------------- 1 | # SamplePlugin user guide 2 | 3 | SamplePlugin is a dummy RMD plugin that can be used as a reference or template for plugin development. 4 | 5 | ## Building, configuring and launching 6 | 7 | SamplePlugin is a simple Go project to be compiled into loadable *.so* file. It's only dependency is [Go-Restful](github.com/emicklei/go-restful) project used for handling REST request. Please note that it doesn't depend on any RMD component so it can be build completely out of RMD repository using *make* 8 | 9 | ```shell 10 | make 11 | make install 12 | ``` 13 | 14 | Configuration and launching of this plugin consist of following steps: 15 | 16 | 1. Add plugin to the list of enabled plugins in *rmd.toml* (section: [default] parameter: *plugins*) 17 | ```toml 18 | [default] 19 | # ... 20 | # ... 21 | plugins = "sampleplugin" 22 | ``` 23 | 2. Add new configuration section [sampleplugin] with plugin configuration 24 | ```toml 25 | [sampleplugin] 26 | # path where compiled library is stored 27 | path = "/etc/rmd/plugins/sampleplugin.so" 28 | # parameter used to show how initialization works, any positive integer value is OK 29 | identifier = 1234 30 | ``` 31 | 3. Restart RMD (if running) 32 | 33 | ## Using REST API endpoint 34 | 35 | This SamplePlugin provides two REST endpoints: */sampleplugin* and */sampleplugin/status*. First one returns overall plugin information (name hardcoded in implementation, and identifier from config file). Second endpoint returns only a plugins' status (one boolean, should be true in properly working and initialized module). As stated in RMD *PluginDevelopmentGuide* these endpoints are prepared only to handle GET requests. 36 | 37 | Below please find sample REST calls with returned results: 38 | 39 | ``` 40 | $> curl -X GET http://127.0.0.1:8081/v1/sampleplugin 41 | { 42 | "name": "sampleplugin", 43 | "id": 1234 44 | }[ 45 | 46 | $> curl -X GET http://127.0.0.1:8081/v1/sampleplugin/status 47 | { 48 | "status": true 49 | } 50 | ``` 51 | 52 | ## Using inside RMD workload 53 | 54 | This plugin does not access any platform resources to make it's implementation as simple as possible but still can be used as a part or RMD *Workload*. 55 | 56 | To create *workload* with *sampleplugin* usage please send POST request with *plugins* section containing parameters for *sampleplugin*. To show how param validation works two parameters were introduced: 57 | 58 | * *name* - parameter of type string, cannot contain spaces and tabulation characters (as this will fail validation), optional 59 | * *value* - real number (with a decimal point), mandatory 60 | 61 | Please note that these parameters have no impact on *Enforce()* function execution - they're just verified and rejected if invalid. Below please find sample POST requests: 62 | 63 | 64 | * with valid set of params (all params defined) 65 | ``` 66 | $> curl -X POST -H "Content-Type: application/json" --data '{ 67 | "core_ids":["10"], "plugins" : { 68 | "sampleplugin" : { 69 | "name" : "somestring", "value" : 12.5 70 | } 71 | } 72 | }' http://127.0.0.1:8081/v1/workloads 73 | ``` 74 | * valid set of params (only mandatory param defined) 75 | ``` 76 | $> curl -X POST -H "Content-Type: application/json" --data '{ 77 | "core_ids":["10"], "plugins" : { 78 | "sampleplugin" : { 79 | "name" : "somestring", "value" : 12.5 80 | } 81 | } 82 | }' http://127.0.0.1:8081/v1/workloads 83 | ``` 84 | * invalid set of params (invalid type) 85 | ``` 86 | $> curl -X POST -H "Content-Type: application/json" --data '{ 87 | "core_ids":["10"], "plugins" : { 88 | "sampleplugin" : { 89 | "name" : "somestring", "value" : "1234" 90 | } 91 | } 92 | }' http://127.0.0.1:8081/v1/workloads 93 | ``` 94 | * invalid set of params (missing mandatory param) 95 | ``` 96 | $> curl -X POST -H "Content-Type: application/json" --data '{ 97 | "core_ids":["10"], "plugins" : { 98 | "sampleplugin" : { 99 | "name" : "somestring" 100 | } 101 | } 102 | }' http://127.0.0.1:8081/v1/workloads 103 | ``` 104 | 105 | -------------------------------------------------------------------------------- /external/sampleplugin/internal.go: -------------------------------------------------------------------------------- 1 | // This file contains some internal structures and functions used by ModuleInterface implementation 2 | // or REST handlers implementation. It's content is not exported neither directly accessible from 3 | // the outside. 4 | 5 | package main 6 | 7 | import ( 8 | "errors" 9 | "strings" 10 | ) 11 | 12 | type inputData struct { 13 | name string 14 | value float64 15 | } 16 | 17 | func convertParamsToData(params map[string]interface{}) (inputData, error) { 18 | result := inputData{} 19 | if len(params) == 0 { 20 | return inputData{}, errors.New("No params given") 21 | } 22 | // check if mandatory 'value' param exists 23 | valIface, ok := params["value"] 24 | if !ok { 25 | return inputData{}, errors.New("Lack of 'value' input param") 26 | } 27 | // check type of param 28 | valFloat, ok := valIface.(float64) 29 | if !ok { 30 | return inputData{}, errors.New("Invalid type of 'value' input param") 31 | } 32 | result.value = valFloat 33 | 34 | // check optional 'name' param 35 | nameIface, ok := params["name"] 36 | if ok { 37 | nameString, ok := nameIface.(string) 38 | if !ok { 39 | // optional param defined but has incorrect type 40 | return inputData{}, errors.New("Invalid type of 'name' input param") 41 | } 42 | if strings.ContainsAny(nameString, " \t") { 43 | return inputData{}, errors.New("Invalid content of 'name' input param") 44 | } 45 | result.name = nameString 46 | } 47 | 48 | return result, nil 49 | } 50 | -------------------------------------------------------------------------------- /external/sampleplugin/rest.go: -------------------------------------------------------------------------------- 1 | // This file contains implementation of REST handlers invoked by HandleRequest() method 2 | // of ModuleInterface implemented in interface.go 3 | 4 | package main 5 | 6 | import ( 7 | "github.com/emicklei/go-restful" 8 | ) 9 | 10 | // ResultID is a struct used in REST handler for /sampleplugin 11 | type ResultID struct { 12 | Name string `json:"name"` 13 | Identifier int64 `json:"id"` 14 | } 15 | 16 | // ResultStatus is a struct used in REST handler for /sampleplugin/status 17 | type ResultStatus struct { 18 | Status bool `json:"status"` 19 | } 20 | 21 | // Two functions below are not part of module interface. Also they don't have to be exported. 22 | // They are added to "module" struct for easier access of module's internal values 23 | 24 | func (m *module) handleMain(request *restful.Request, response *restful.Response) { 25 | stat := ResultID{Name: "sampleplugin", Identifier: m.identifier} 26 | response.WriteEntity(stat) 27 | } 28 | 29 | func (m *module) handleStatus(request *restful.Request, response *restful.Response) { 30 | response.WriteEntity(ResultStatus{Status: m.initialized}) 31 | } 32 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/intel/rmd 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f // indirect 7 | github.com/casbin/casbin v1.9.1 8 | github.com/emicklei/go-restful v2.4.0+incompatible 9 | github.com/etcd-io/bbolt v1.3.3 10 | github.com/fatih/structs v1.1.0 // indirect 11 | github.com/fsnotify/fsnotify v1.4.9 12 | github.com/gavv/monotime v0.0.0-20161010190848-47d58efa6955 // indirect 13 | github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 14 | github.com/gobwas/glob v0.2.3 15 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b 16 | github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect 17 | github.com/imkira/go-interpol v1.1.0 // indirect 18 | github.com/jarcoal/httpmock v1.0.4 19 | github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect 20 | github.com/klauspost/compress v1.10.6 // indirect 21 | github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd // indirect 22 | github.com/moul/http2curl v0.0.0-20161031194548-4e24498b31db // indirect 23 | github.com/onsi/ginkgo v1.14.0 24 | github.com/onsi/gomega v1.10.1 25 | github.com/pelletier/go-buffruneio v0.2.0 // indirect 26 | github.com/prashantv/gostub v0.0.0-20170112001514-5c68b99bb088 27 | github.com/sergi/go-diff v0.0.0-20170409071739-feef008d51ad // indirect 28 | github.com/sirupsen/logrus v1.6.0 29 | github.com/smartystreets/assertions v1.0.1 // indirect 30 | github.com/smartystreets/goconvey v1.6.4 31 | github.com/spf13/pflag v1.0.5 32 | github.com/spf13/viper v1.7.0 33 | github.com/valyala/bytebufferpool v1.0.0 // indirect 34 | github.com/valyala/fasthttp v0.0.0-20170721134547-ae643c872d2c // indirect 35 | github.com/xeipuuv/gojsonschema v1.2.0 // indirect 36 | github.com/yalp/jsonpath v0.0.0-20150812003900-31a79c7593bb // indirect 37 | github.com/yudai/gojsondiff v1.0.0 // indirect 38 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect 39 | github.com/yudai/pp v2.0.1+incompatible // indirect 40 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 41 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect 42 | gopkg.in/gavv/httpexpect.v1 v1.0.0-20170328075429-d852bb8255b6 43 | ) 44 | -------------------------------------------------------------------------------- /internal/db/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | // Database struct 10 | type Database struct { 11 | Backend string `toml:"backend"` 12 | Transport string `toml:"transport"` 13 | DBName string `toml:"dbname"` 14 | } 15 | 16 | var configOnce sync.Once 17 | 18 | var db = &Database{"bolt", "/var/run/rmd/rmd.db", "rmd"} 19 | 20 | // NewConfig is Concurrency safe. 21 | func NewConfig() Database { 22 | configOnce.Do(func() { 23 | // FIXME , we are planing to use logrusHelper. Seems we still 24 | // need missing some initialization for logrus. But it repors error as 25 | // follow: 26 | // # github.com/heirko/go-contrib/logrusHelper 27 | // undefined: logrus_mate.LoggerConfig 28 | // var c = logrusHelper.UnmarshalConfiguration(viper) // Unmarshal configuration from Viper 29 | // logrusHelper.SetConfig(logrus.StandardLogger(), c) // for e.g. apply it to logrus default instance 30 | 31 | viper.UnmarshalKey("database", db) 32 | }) 33 | 34 | return *db 35 | } 36 | -------------------------------------------------------------------------------- /internal/db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | // from app import an config is really not a good idea. 8 | // uncouple it from APP. Or we can add it in a rmd/config 9 | "github.com/intel/rmd/internal/db/config" 10 | wltypes "github.com/intel/rmd/modules/workload/types" 11 | util "github.com/intel/rmd/utils" 12 | libutil "github.com/intel/rmd/utils/bitmap" 13 | ) 14 | 15 | // WorkloadTableName is the table name for workload 16 | const WorkloadTableName = "workload" 17 | 18 | // MappingTableName contains mapping between UUID and WorkloadID 19 | const MappingTableName = "mapping" 20 | 21 | // DB is the interface for a db engine 22 | type DB interface { 23 | Initialize(transport, dbname string) error 24 | CreateWorkload(w *wltypes.RDTWorkLoad) error 25 | DeleteWorkload(w *wltypes.RDTWorkLoad) error 26 | UpdateWorkload(w *wltypes.RDTWorkLoad) error 27 | GetAllWorkload() ([]wltypes.RDTWorkLoad, error) 28 | GetWorkloadByID(id string) (wltypes.RDTWorkLoad, error) 29 | GetWorkloadByUUID(id string) (wltypes.RDTWorkLoad, error) 30 | ValidateWorkload(w *wltypes.RDTWorkLoad) error 31 | QueryWorkload(query map[string]interface{}) ([]wltypes.RDTWorkLoad, error) 32 | } 33 | 34 | // NewDB return DB connection 35 | func NewDB() (DB, error) { 36 | dbcon := config.NewConfig() 37 | if dbcon.Backend == "bolt" { 38 | return newBoltDB() 39 | } else if dbcon.Backend == "mgo" { 40 | //return newMgoDB() commented out now. 41 | return nil, fmt.Errorf("Mongo DB is not currently supported in RMD: Select bolt backend db") 42 | } else { 43 | return nil, fmt.Errorf("Unsupported DB backend %s", dbcon.Backend) 44 | } 45 | } 46 | 47 | // this function does 3 things to validate a user request workload is 48 | // validate at data base layer 49 | func validateWorkload(w wltypes.RDTWorkLoad, ws []wltypes.RDTWorkLoad) error { 50 | 51 | if len(w.ID) == 0 && len(w.TaskIDs) == 0 && len(w.CoreIDs) == 0 { 52 | return errors.New("Incomplete Workload definition") 53 | } 54 | 55 | // User post a workload id/uuid in it's request 56 | if w.ID != "" { 57 | for _, i := range ws { 58 | if w.ID == i.ID { 59 | return fmt.Errorf("Workload with id %s already exists", w.ID) 60 | } 61 | } 62 | } 63 | 64 | // User post a workload id/uuid in it's request 65 | if w.UUID != "" { 66 | for _, i := range ws { 67 | if w.UUID == i.UUID { 68 | return fmt.Errorf("UUID %s already exists in workload %s", w.UUID, i.ID) 69 | } 70 | } 71 | } 72 | 73 | // Validate if the task id of workload has existed. 74 | for _, t := range w.TaskIDs { 75 | for _, wi := range ws { 76 | if util.HasElem(wi.TaskIDs, t) { 77 | return fmt.Errorf("TaskID %s already exists in workload %s", t, wi.ID) 78 | } 79 | } 80 | } 81 | 82 | if len(w.CoreIDs) == 0 { 83 | return nil 84 | } 85 | 86 | // Validate if the core id of workload has overlap with current ones. 87 | bm, err := libutil.NewBitmap(w.CoreIDs) 88 | if err != nil { 89 | return err 90 | } 91 | bmsum, err := libutil.NewBitmap("") 92 | if err != nil { 93 | return err 94 | } 95 | for _, c := range ws { 96 | if len(c.CoreIDs) > 0 { 97 | tmpbm, err := libutil.NewBitmap(c.CoreIDs) 98 | if err != nil { 99 | return err 100 | } 101 | bmsum = bmsum.Or(tmpbm) 102 | } 103 | } 104 | 105 | bminter := bm.And(bmsum) 106 | 107 | var coreBits uint32 108 | if bminter.Len > 0 { 109 | coreBits = uint32(bminter.Bits[0]) 110 | } 111 | var cores = []string{} 112 | if bminter.Len > 0 && coreBits > 0 { 113 | var bit uint32 114 | for bit = 0; bit < uint32(bminter.Len); bit++ { 115 | if (coreBits % 2) == 1 { 116 | cores = append(cores, fmt.Sprint(bit)) 117 | } 118 | coreBits = coreBits / 2 119 | } 120 | } 121 | 122 | if !bminter.IsEmpty() { 123 | return fmt.Errorf("CPU list %s has been assigned", cores) 124 | } 125 | 126 | return nil 127 | } 128 | -------------------------------------------------------------------------------- /internal/db/mgodb.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | 7 | "github.com/globalsign/mgo" 8 | "github.com/globalsign/mgo/bson" 9 | 10 | "github.com/intel/rmd/internal/db/config" 11 | wltypes "github.com/intel/rmd/modules/workload/types" 12 | ) 13 | 14 | // mgo database session 15 | var mgoSession *mgo.Session 16 | var mgoSessionOnce sync.Once 17 | 18 | // Dbname is database name of mgodb 19 | var Dbname string 20 | 21 | // MgoDB is connection of mgodb 22 | type MgoDB struct { 23 | session *mgo.Session 24 | } 25 | 26 | // We thought, open a file, means open a session. 27 | // Unity Concept with mongodb 28 | func getMgoSession() error { 29 | var err error 30 | mgoSessionOnce.Do(func() { 31 | conf := config.NewConfig() 32 | mgoSession, err = mgo.Dial(conf.Transport) 33 | }) 34 | return err 35 | } 36 | 37 | func closeMgoSession() { 38 | } 39 | 40 | func newMgoDB() (DB, error) { 41 | var db MgoDB 42 | if err := getSession(); err != nil { 43 | return &db, err 44 | } 45 | db.session = mgoSession 46 | return &db, nil 47 | 48 | } 49 | 50 | // Initialize does initialize 51 | func (m *MgoDB) Initialize(transport, dbname string) error { 52 | 53 | conf := config.NewConfig() 54 | // FIXME, Dbname here seems some urgly 55 | Dbname = conf.DBName 56 | 57 | c := m.session.DB(Dbname).C(WorkloadTableName) 58 | if c == nil { 59 | return errors.New("Unable to create collection RDTpolicy") 60 | } 61 | 62 | index := mgo.Index{ 63 | Key: []string{"ID"}, 64 | Unique: true, 65 | } 66 | 67 | err := c.EnsureIndex(index) 68 | 69 | if err != nil { 70 | return err 71 | } 72 | return nil 73 | } 74 | 75 | // ValidateWorkload from data base view 76 | func (m *MgoDB) ValidateWorkload(w *wltypes.RDTWorkLoad) error { 77 | /* When create a new workload we need to verify that the new PIDs 78 | we the workload specified should not existed */ 79 | // not implement yet 80 | return nil 81 | } 82 | 83 | // CreateWorkload creates workload in db 84 | func (m *MgoDB) CreateWorkload(w *wltypes.RDTWorkLoad) error { 85 | s := m.session.Copy() 86 | defer s.Close() 87 | 88 | return s.DB(Dbname).C(WorkloadTableName).Insert(w) 89 | } 90 | 91 | // DeleteWorkload removes workload from db 92 | func (m *MgoDB) DeleteWorkload(w *wltypes.RDTWorkLoad) error { 93 | // not implement yet 94 | return nil 95 | } 96 | 97 | // UpdateWorkload updates 98 | func (m *MgoDB) UpdateWorkload(w *wltypes.RDTWorkLoad) error { 99 | // not implement yet 100 | return nil 101 | } 102 | 103 | // GetAllWorkload returns all workloads in db 104 | func (m *MgoDB) GetAllWorkload() ([]wltypes.RDTWorkLoad, error) { 105 | ws := []wltypes.RDTWorkLoad{} 106 | s := m.session.Copy() 107 | defer s.Close() 108 | 109 | if err := s.DB(Dbname).C(WorkloadTableName).Find(nil).All(&ws); err != nil { 110 | return ws, err 111 | } 112 | 113 | return ws, nil 114 | } 115 | 116 | // GetWorkloadByID by ID 117 | func (m *MgoDB) GetWorkloadByID(id string) (wltypes.RDTWorkLoad, error) { 118 | w := wltypes.RDTWorkLoad{} 119 | s := m.session.Copy() 120 | defer s.Close() 121 | 122 | if err := s.DB(Dbname).C(WorkloadTableName).Find(bson.M{"ID": w.ID}).One(&w); err != nil { 123 | return w, err 124 | } 125 | 126 | return w, nil 127 | 128 | } 129 | 130 | // QueryWorkload with given params 131 | func (m *MgoDB) QueryWorkload(query map[string]interface{}) ([]wltypes.RDTWorkLoad, error) { 132 | // not implement yet 133 | return []wltypes.RDTWorkLoad{}, nil 134 | } 135 | 136 | // GetWorkloadByUUID Returns workload specified by UUID (if such exists in DB) 137 | func (m *MgoDB) GetWorkloadByUUID(id string) (wltypes.RDTWorkLoad, error) { 138 | w := wltypes.RDTWorkLoad{} 139 | return w, errors.New("Not yet implemented") 140 | } 141 | -------------------------------------------------------------------------------- /internal/error/error.go: -------------------------------------------------------------------------------- 1 | package error 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // possible error codes for AppError implementation 8 | const ( 9 | BadRequest int = 0 10 | InternalServer int = 1 11 | NotFound int = 2 12 | Invalid int = 3 13 | ) 14 | 15 | // AppError is struct of application error 16 | // The Code should be a const in https://golang.org/pkg/net/http/ 17 | type AppError struct { 18 | Code int 19 | Message string 20 | Err error 21 | } 22 | 23 | // Error gives error string 24 | func (e AppError) Error() string { 25 | if e.Message != "" && e.Err != nil { 26 | return fmt.Sprintf("%s. Details: %v\n", e.Message, e.Err) 27 | } 28 | if e.Err != nil { 29 | return e.Err.Error() 30 | } 31 | return e.Message 32 | } 33 | 34 | // AppErrorf create AppError from formate 35 | func AppErrorf(code int, format string, a ...interface{}) *AppError { 36 | msg := fmt.Sprintf(format, a...) 37 | ae := AppError{code, msg, nil} 38 | return &ae 39 | } 40 | 41 | // NewAppError create new AppError 42 | func NewAppError(value ...interface{}) *AppError { 43 | ae := AppError{} 44 | for i, val := range value { 45 | if i >= 3 { 46 | break 47 | } 48 | switch v := val.(type) { 49 | case int: 50 | ae.Code = v 51 | case string: 52 | ae.Message = v 53 | case error: 54 | ae.Err = v 55 | default: 56 | ae.Message = "Unknown AppError type!" 57 | } 58 | } 59 | if ae.Code == 0 { 60 | ae.Code = InternalServer 61 | } 62 | return &ae 63 | } 64 | -------------------------------------------------------------------------------- /internal/error/error_test.go: -------------------------------------------------------------------------------- 1 | package error 2 | 3 | import ( 4 | "testing" 5 | 6 | "errors" 7 | "fmt" 8 | "net/http" 9 | ) 10 | 11 | func TestAppError(t *testing.T) { 12 | err := func() error { 13 | return errors.New("Return a error just for test") 14 | } 15 | // In a model, after we catch an error, reform it and return AppError. 16 | appErr := AppError{http.StatusNotFound, "Reason Message", err()} 17 | fmt.Printf("%v", appErr) 18 | fmt.Println(appErr.Error(), appErr.Code) 19 | 20 | ep := NewAppError(http.StatusNotFound, err(), "Reason Message") 21 | fmt.Println(ep.Err, ep.Code) 22 | // build from formated string 23 | e := AppErrorf(ep.Code, "Error for test, reason: %d\n", ep.Code) 24 | fmt.Printf("%T, %s\n", e, e.Error()) 25 | // Output: 26 | // {404 Reason Message. Return a error just for test.} 27 | // Reason Message.. Details: Return a error just for test. 404 28 | // Return a error just for test. 404 29 | // *error.AppError, Error for test, reason: 404 30 | } 31 | -------------------------------------------------------------------------------- /internal/inventory/inventory.go: -------------------------------------------------------------------------------- 1 | package inventory 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/emicklei/go-restful" 7 | ) 8 | 9 | const endpointPath = "/v1/inventory" 10 | 11 | // Inventory invenotry struct 12 | type Inventory struct { 13 | } 14 | 15 | // Capability represents capability and its availability 16 | type Capability struct { 17 | Name string `json:"name"` 18 | Available bool `json:"available"` 19 | } 20 | 21 | // Register registers REST endpoint 22 | func (i *Inventory) Register(container *restful.Container) { 23 | ws := new(restful.WebService) 24 | ws. 25 | Path(endpointPath). 26 | Doc("Show the system hardware inventory and capabilities"). 27 | Consumes(restful.MIME_JSON). 28 | Produces(restful.MIME_JSON) 29 | 30 | // register handlers for supported GET actions 31 | ws.Route(ws.GET("/").To(i.GetCapabilities). 32 | Doc("Get the pstates for all CPU cores"). 33 | Operation("PstateGetAll")) 34 | 35 | container.Add(ws) 36 | } 37 | 38 | // GetCapabilities return inventory capabilites 39 | func (i *Inventory) GetCapabilities(request *restful.Request, response *restful.Response) { 40 | // response.WriteError(http.StatusNoContent, nil) 41 | result := []Capability{} 42 | 43 | // call each inventory checking function and add returned data to result 44 | result = append(result, CheckRDT(), CheckScaling()) 45 | 46 | response.WriteHeaderAndEntity(http.StatusOK, result) 47 | } 48 | -------------------------------------------------------------------------------- /internal/inventory/pstate.go: -------------------------------------------------------------------------------- 1 | package inventory 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "sync" 7 | 8 | log "github.com/sirupsen/logrus" 9 | ) 10 | 11 | const scalingDriverPath = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver" 12 | const bufSize = 128 13 | 14 | var supportedDrivers = []string{"acpi", "intel_pstate"} 15 | 16 | var scalingOneGuard sync.Once 17 | var scalingResult Capability 18 | 19 | // CheckScaling checks if proper CPU freq. scaling driver is available 20 | func CheckScaling() Capability { 21 | scalingOneGuard.Do(func() { 22 | scalingResult = Capability{Name: "pstate", Available: checkPstateAvailability()} 23 | }) 24 | 25 | return scalingResult 26 | } 27 | 28 | func checkPstateAvailability() bool { 29 | if _, err := os.Stat(scalingDriverPath); os.IsNotExist(err) { 30 | // file does not exists - cannot check scaling driver 31 | log.Error("Unable to check scaling driver!") 32 | return false 33 | } 34 | 35 | input, err := os.Open(scalingDriverPath) 36 | if err != nil { 37 | log.Error("Unable to open scaling driver file!") 38 | return false 39 | } 40 | defer input.Close() 41 | 42 | buffer := make([]byte, bufSize) 43 | num, err := input.Read(buffer) 44 | if err != nil { 45 | log.Error("Unable to read scaling driver file!") 46 | return false 47 | } 48 | driver := strings.Trim(string(buffer[:num]), " \n") 49 | 50 | for _, supported := range supportedDrivers { 51 | if driver == supported { 52 | log.Debug("Supported driver detected: ", supported) 53 | return true 54 | } 55 | } 56 | 57 | log.Debug("Unsupported scaling driver: ", driver) 58 | return false 59 | } 60 | -------------------------------------------------------------------------------- /internal/inventory/rdt_cat.go: -------------------------------------------------------------------------------- 1 | package inventory 2 | 3 | import ( 4 | "bufio" 5 | "io/ioutil" 6 | "os" 7 | "strings" 8 | "sync" 9 | 10 | log "github.com/sirupsen/logrus" 11 | ) 12 | 13 | const ( 14 | kernelCmdPath = "/proc/cmdline" 15 | kernelCmdPattern = "rdt=l3cat" 16 | cpuInfoPath = "/proc/cpuinfo" 17 | cpuInfoPattern1 = "rdt_a" 18 | cpuInfoPattern2 = "cat_l3" 19 | fsInfoPath = "/proc/filesystems" 20 | fsInfoPattern = "resctrl" 21 | ) 22 | 23 | var rdtOneGuard sync.Once 24 | var rdtResult Capability 25 | 26 | // CheckRDT checks if CAT_RDT support exist on host 27 | func CheckRDT() Capability { 28 | rdtOneGuard.Do(func() { 29 | rdtResult = Capability{Name: "pstate", Available: false} 30 | // check platform 31 | rdtResult.Available = checkKernelCmd() && checkCPUFlag() && checkResctrlFs() 32 | }) 33 | 34 | return rdtResult 35 | } 36 | 37 | func checkKernelCmd() bool { 38 | lineBuffer := make([]byte, 1024) 39 | // check kernel flag 40 | kernFile, err := os.Open(kernelCmdPath) 41 | if err != nil { 42 | log.Error("Failed to check kernel params: ", err.Error()) 43 | return false 44 | } 45 | defer kernFile.Close() 46 | 47 | cnt, err := kernFile.Read(lineBuffer) 48 | if err != nil { 49 | log.Error("Failed to check kernel params: ", err.Error()) 50 | return false 51 | } 52 | kernelCmdLine := string(lineBuffer[:cnt]) 53 | if strings.Contains(kernelCmdLine, kernelCmdPattern) != true { 54 | log.Debug("Kernel command line does not contain: ", kernelCmdPattern) 55 | return false 56 | } 57 | 58 | return true 59 | } 60 | 61 | func checkCPUFlag() bool { 62 | // check CPU flag 63 | cpuFile, err := os.Open(cpuInfoPath) 64 | if err != nil { 65 | log.Error("Failed to check cpu flags: ", err.Error()) 66 | return false 67 | } 68 | defer cpuFile.Close() 69 | 70 | scanner := bufio.NewScanner(cpuFile) 71 | for scanner.Scan() != false { 72 | line := scanner.Text() 73 | if strings.HasPrefix(line, "flags") { 74 | flags := strings.Split(line, ":") 75 | // should never happen but better check it 76 | if len(flags) != 2 { 77 | log.Error("Broken CPU flags") 78 | return false 79 | } 80 | if strings.Contains(line, cpuInfoPattern1) != true { 81 | log.Debug("Missing CPU flag: ", cpuInfoPattern1) 82 | return false 83 | } 84 | if strings.Contains(line, cpuInfoPattern2) != true { 85 | log.Debug("Missing CPU flag: ", cpuInfoPattern2) 86 | return false 87 | } 88 | // there can be multpiple cores and so multiple flags lines 89 | // but there's no need to analyze all of them 90 | break 91 | } 92 | // skip line processing if it's not the CPU flags line 93 | } 94 | 95 | return true 96 | } 97 | 98 | func checkResctrlFs() bool { 99 | // check resctrl filesystem 100 | fsData, err := ioutil.ReadFile(fsInfoPath) 101 | if err != nil { 102 | log.Error("Failed to read filesystems: ", err.Error()) 103 | return false 104 | } 105 | fsString := string(fsData) 106 | 107 | if strings.Contains(fsString, fsInfoPattern) != true { 108 | log.Error("Filesystem not found: ", fsInfoPattern) 109 | return false 110 | } 111 | 112 | // no error till now -> all necessary items/flags found 113 | return true 114 | } 115 | -------------------------------------------------------------------------------- /internal/openstack/config.go: -------------------------------------------------------------------------------- 1 | // +build openstack 2 | 3 | package openstack 4 | 5 | import ( 6 | "sync" 7 | 8 | log "github.com/sirupsen/logrus" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | // Config represenent configuration for Openstack features 13 | type Config struct { 14 | ProviderConfigPath string `toml:"providerConfigPath"` 15 | AmqpURI string `toml:"amqpuri"` 16 | BindingKey string `toml:"bindingKey"` 17 | KeystoneURL string `toml:"keystoneUrl"` 18 | KeystoneLogin string `toml:"keystoneLogin"` 19 | KeystonePassword string `toml:"keystonePassword"` 20 | } 21 | 22 | var oscfg = &Config{} 23 | var runOnce sync.Once 24 | 25 | // GetConfig reads P-State plugin configuration from configuration file 26 | func GetConfig() *Config { 27 | runOnce.Do(func() { 28 | err := viper.UnmarshalKey("openstack", oscfg) 29 | if err != nil { 30 | log.Error("openstack.GetConfig() error:", err) 31 | } 32 | }) 33 | return oscfg 34 | } 35 | -------------------------------------------------------------------------------- /internal/openstack/openstack_disabled.go: -------------------------------------------------------------------------------- 1 | // +build !openstack 2 | 3 | package openstack 4 | 5 | import ( 6 | "errors" 7 | 8 | log "github.com/sirupsen/logrus" 9 | ) 10 | 11 | // Init function initializes openstack integration features 12 | func Init() error { 13 | log.Error("Calling Openstack initialization while this RMD build does not support OpenStack integration") 14 | 15 | return errors.New("OpenStack enabled but not supported - check logs and config file") 16 | } 17 | -------------------------------------------------------------------------------- /internal/openstack/openstack_enabled.go: -------------------------------------------------------------------------------- 1 | // +build openstack 2 | 3 | package openstack 4 | 5 | import ( 6 | "errors" 7 | "os" 8 | 9 | log "github.com/sirupsen/logrus" 10 | ) 11 | 12 | var hostname string 13 | 14 | // Init function initializes openstack integration features 15 | func Init() error { 16 | //TODO maybe sync DoOnce? 17 | var err error 18 | hostname, err = os.Hostname() 19 | if err != nil { 20 | log.Errorf("Could not get host name, RMD will not be able to filter notifications for current host! %v", err) 21 | return errors.New("Could not get host name") 22 | } 23 | 24 | oscfg := GetConfig() 25 | // root process and rmd user process have to perform different operations 26 | if os.Geteuid() == 0 { 27 | // read OpenStack related configuration 28 | if len(oscfg.ProviderConfigPath) == 0 { 29 | log.Error("No Provider Configuration file path available") 30 | return errors.New("Missing Provider Configuration file path") 31 | } 32 | 33 | // launch Provider Config file generator 34 | if err := GenerateFile(oscfg.ProviderConfigPath); err != nil { 35 | log.Error("Failed to generate provider config file") 36 | return err 37 | } 38 | } else { 39 | // token is needed for authorization and access to an OpenStack environment 40 | getToken() 41 | 42 | // enable OpenStack events listener 43 | if err := NovaListenerStart(oscfg.AmqpURI, oscfg.BindingKey); err != nil { 44 | log.Error("Failed to launch Nova event listener") 45 | return err 46 | } 47 | } 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /internal/openstack/providercfg.go: -------------------------------------------------------------------------------- 1 | // +build openstack 2 | 3 | package openstack 4 | 5 | import ( 6 | "errors" 7 | "io/ioutil" 8 | "strconv" 9 | "strings" 10 | 11 | log "github.com/sirupsen/logrus" 12 | 13 | "github.com/intel/rmd/internal/inventory" 14 | rdtconf "github.com/intel/rmd/modules/cache/config" 15 | yaml "gopkg.in/yaml.v2" 16 | ) 17 | 18 | const ( 19 | schemaVersion = "1.0" 20 | llcSectionTitle = "CUSTOM_LLC" 21 | pstateEnabledFlag = "CUSTOM_P_STATE_ENABLED" 22 | pstateDisabledFlag = "CUSTOM_P_STATE_DISABLED" 23 | cacheWaysInfoPath = "/sys/devices/system/cpu/cpu0/cache/index3/ways_of_associativity" 24 | ) 25 | 26 | type meta struct { 27 | Version string `yaml:"schema_version"` 28 | } 29 | 30 | type customResourceDetails struct { 31 | Total uint `yaml:"total"` 32 | Reserved uint `yaml:"reserved"` 33 | MinUnit uint `yaml:"min_unit"` 34 | MaxUnit uint `yaml:"max_unit"` 35 | StepSize uint `yaml:"step_size"` 36 | AllocationRatio uint `yaml:"allocation_ratio"` 37 | } 38 | 39 | type llcResource struct { 40 | CustomLLC customResourceDetails `yaml:"CUSTOM_LLC"` 41 | } 42 | 43 | type providerEntity struct { 44 | Identification struct { 45 | UUID string `yaml:"uuid"` 46 | } `yaml:"identification"` 47 | Inventories struct { 48 | Additional []llcResource 49 | } `yaml:"inventories"` 50 | Traits struct { 51 | Additional []string `yaml:"additional"` 52 | } `yaml:"traits"` 53 | } 54 | 55 | // GenerateFile Provider Config File generation according to scheme presented here: 56 | // https://github.com/openstack/nova-specs/blob/master/specs/train/approved/provider-config-file.rst 57 | func GenerateFile(filePath string) error { 58 | log.Debug("Generating Provider Config File for Nova") 59 | if len(filePath) == 0 { 60 | log.Error("Invalid (empty) file path") 61 | return errors.New("Invalid (empty) file path") 62 | } 63 | 64 | cacheAvailable := inventory.CheckRDT() 65 | pstateAvailabie := inventory.CheckScaling() 66 | 67 | providerData := make(map[string]interface{}) 68 | 69 | // add schema information 70 | mt := meta{Version: schemaVersion} 71 | providerData["meta"] = mt 72 | 73 | // add providers 74 | var entity providerEntity 75 | var llcRes llcResource 76 | 77 | entity.Identification.UUID = "$COMPUTE_NODE" 78 | 79 | if cacheAvailable.Available { 80 | // read total number of cache ways 81 | buffer, err := ioutil.ReadFile(cacheWaysInfoPath) 82 | if err != nil { 83 | log.Error("Failed to read cache ways info: ", err.Error()) 84 | return err 85 | } 86 | total, err := strconv.ParseInt(strings.Trim(string(buffer), " \n"), 10, 32) 87 | if err != nil { 88 | log.Error("Invalid number of cache ways in file: ", err.Error()) 89 | return err 90 | } 91 | llcRes.CustomLLC.Total = uint(total) // number cache ways read from system 92 | 93 | // fetch RDTpool OS (reserved) config 94 | cfg := rdtconf.NewOSConfig() 95 | llcRes.CustomLLC.Reserved = cfg.CacheWays // OS group 96 | 97 | llcRes.CustomLLC.MinUnit = 1 // const 98 | llcRes.CustomLLC.MaxUnit = llcRes.CustomLLC.Total - llcRes.CustomLLC.Reserved // FIXME, should be max of one socket (minimum of all available sockets) 99 | llcRes.CustomLLC.StepSize = 1 // const 100 | llcRes.CustomLLC.AllocationRatio = 1 // const 101 | } 102 | entity.Inventories.Additional = []llcResource{llcRes} 103 | 104 | if pstateAvailabie.Available { 105 | // add P-State enabled flag 106 | entity.Traits.Additional = []string{pstateEnabledFlag} 107 | } else { 108 | // add P-State disabled flag 109 | entity.Traits.Additional = []string{pstateDisabledFlag} 110 | } 111 | 112 | providerData["providers"] = []providerEntity{entity} 113 | 114 | // convert providerData to yaml format 115 | buffer, err := yaml.Marshal(providerData) 116 | if err != nil { 117 | log.Error("Failed to prepare content for Provider Config File: ", err.Error()) 118 | } else { 119 | err = ioutil.WriteFile(filePath, buffer, 0644) 120 | if err != nil { 121 | log.Error("Failed to write Provider Config File: ", err.Error()) 122 | } 123 | } 124 | return err 125 | } 126 | -------------------------------------------------------------------------------- /internal/openstack/util.go: -------------------------------------------------------------------------------- 1 | // + build linux,openstack 2 | 3 | package openstack 4 | 5 | import ( 6 | "os/exec" 7 | "strconv" 8 | "strings" 9 | 10 | log "github.com/sirupsen/logrus" 11 | ) 12 | 13 | // getPIDByName finds PID based on part of process cmdline 14 | func getPIDByName(name string) (string, error) { 15 | out, err := exec.Command("pgrep", "-f", name).Output() 16 | if err != nil { 17 | log.Error(err) 18 | return "", err 19 | } 20 | result := strings.Trim(string(out), " \n") 21 | return result, nil 22 | } 23 | 24 | // getPIDTaskSet returns list of pinned cores for given PID 25 | func getPIDTaskSet(pid string) ([]string, error) { 26 | out, err := exec.Command("taskset", "-p", pid).Output() 27 | if err != nil { 28 | log.Error(err) 29 | return nil, err 30 | } 31 | words := strings.Fields(string(out)) 32 | mask := words[len(words)-1] 33 | mask = strings.ToLower(mask) 34 | 35 | //do not parse the mask if no affinity is set (all letters are f) 36 | if strings.Count(mask, "f") != len(mask) { 37 | return parseTasksetMask(mask), nil 38 | } 39 | 40 | return []string{}, nil 41 | } 42 | 43 | // parseTasksetMask returns list of set bit indices for given mask 44 | func parseTasksetMask(mask string) []string { 45 | ret := []string{} 46 | l := uint(len(mask)) - 1 47 | for i := uint(0); i <= l; i++ { 48 | //start from the end of mask, and parse feach 'hex letter' separately 49 | val, err := strconv.ParseInt(string(mask[l-i]), 16, 16) 50 | if err != nil { 51 | log.Println("Failed to parse mask") 52 | return []string{} 53 | } 54 | for j := uint(0); j < 4; j++ { 55 | if ((1 << j) & val) != 0 { 56 | ret = append(ret, strconv.FormatUint(uint64(i*4+j), 10)) 57 | } 58 | } 59 | } 60 | return ret 61 | } 62 | -------------------------------------------------------------------------------- /internal/openstack/util_test.go: -------------------------------------------------------------------------------- 1 | // + build linux,openstack 2 | 3 | package openstack 4 | 5 | import ( 6 | "reflect" 7 | "strconv" 8 | "testing" 9 | ) 10 | 11 | func Test_parseTasksetMask(t *testing.T) { 12 | cores := []string{} 13 | for i := 0; i < 2048; i++ { 14 | cores = append(cores, strconv.Itoa(i)) 15 | } 16 | mask := "" 17 | for i := 0; i < 512; i++ { 18 | mask += "F" 19 | } 20 | 21 | type args struct { 22 | mask string 23 | } 24 | tests := []struct { 25 | name string 26 | args args 27 | want []string 28 | }{ 29 | {"8 bits set", args{"FF"}, []string{"0", "1", "2", "3", "4", "5", "6", "7"}}, 30 | {"1 bits set", args{"01"}, []string{"0"}}, 31 | {"1 bits set", args{"0E"}, []string{"1", "2", "3"}}, 32 | {"2 bits set", args{"11"}, []string{"0", "4"}}, 33 | {"2048 bits set", args{mask}, cores}, 34 | } 35 | for _, tt := range tests { 36 | t.Run(tt.name, func(t *testing.T) { 37 | if got := parseTasksetMask(tt.args.mask); !reflect.DeepEqual(got, tt.want) { 38 | t.Errorf("parseTasksetMask() = %v, want %v", got, tt.want) 39 | } 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /internal/pam/callback.go: -------------------------------------------------------------------------------- 1 | // SPDX short identifier: BSD-2-Clause 2 | // Copyright 2011, krockot 3 | // Copyright 2015, Michael Steinert 4 | // All rights reserved. 5 | 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | 9 | // * Redistributions of source code must retain the above copyright notice, this 10 | // list of conditions and the following disclaimer. 11 | 12 | // * Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package pam 28 | 29 | import "sync" 30 | 31 | var cb struct { 32 | sync.Mutex 33 | m map[int]interface{} 34 | c int 35 | } 36 | 37 | func init() { 38 | cb.m = make(map[int]interface{}) 39 | } 40 | 41 | func cbAdd(v interface{}) int { 42 | cb.Lock() 43 | defer cb.Unlock() 44 | cb.c++ 45 | cb.m[cb.c] = v 46 | return cb.c 47 | } 48 | 49 | func cbGet(c int) interface{} { 50 | cb.Lock() 51 | defer cb.Unlock() 52 | if v, ok := cb.m[c]; ok { 53 | return v 54 | } 55 | panic("Callback pointer not found") 56 | } 57 | 58 | func cbDelete(c int) { 59 | cb.Lock() 60 | defer cb.Unlock() 61 | if _, ok := cb.m[c]; !ok { 62 | panic("Callback pointer not found") 63 | } 64 | delete(cb.m, c) 65 | } 66 | -------------------------------------------------------------------------------- /internal/pam/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | // PAM is the configuration option 10 | type PAM struct { 11 | Service string `toml:"service"` 12 | } 13 | 14 | var once sync.Once 15 | var pam = &PAM{"rmd"} 16 | 17 | // GetPAMConfig reads config from config file 18 | func GetPAMConfig() *PAM { 19 | once.Do(func() { 20 | viper.UnmarshalKey("pam", pam) 21 | }) 22 | return pam 23 | } 24 | -------------------------------------------------------------------------------- /internal/pam/pam.go: -------------------------------------------------------------------------------- 1 | package pam 2 | 3 | import ( 4 | "errors" 5 | "github.com/intel/rmd/internal/pam/config" 6 | log "github.com/sirupsen/logrus" 7 | ) 8 | 9 | // Credential represents user provided credential 10 | type Credential struct { 11 | Username string 12 | Password string 13 | } 14 | 15 | // PAMResponseHandler handles the communication between PAM client and PAM module 16 | func (c Credential) PAMResponseHandler(s ConvResponse, msg string) (string, error) { 17 | switch s { 18 | case PromptEchoOff: 19 | return c.Password, nil 20 | case PromptEchoOn: 21 | log.Info(msg) 22 | return c.Password, nil 23 | case ErrorMsg: 24 | log.Error(msg) 25 | return "", nil 26 | case TextInfo: 27 | log.Info(msg) 28 | return "", nil 29 | default: 30 | return "", errors.New("unrecognized conversation message style") 31 | } 32 | } 33 | 34 | // pamTxAuthenticate authenticates a PAM transaction 35 | func pamTxAuthenticate(transaction *Transaction) error { 36 | err := transaction.Authenticate(0) 37 | EndTransaction(transaction) 38 | return err 39 | } 40 | 41 | // PAMAuthenticate performs PAM authentication for the user credentials provided 42 | func (c Credential) PAMAuthenticate() error { 43 | tx, err := c.PAMStartFunc() 44 | if err != nil { 45 | return err 46 | } 47 | err = pamTxAuthenticate(tx) 48 | return err 49 | } 50 | 51 | // pamStartFunc starts the conversation between PAM client and PAM module 52 | func pamStartFunc(service string, user string, handler func(ConvResponse, string) (string, error)) (*Transaction, error) { 53 | tx, err := StartFunc(service, user, handler) 54 | if err != nil { 55 | return nil, err 56 | } 57 | return tx, nil 58 | } 59 | 60 | // PAMStartFunc establishes the connection to PAM module 61 | func (c Credential) PAMStartFunc() (*Transaction, error) { 62 | return pamStartFunc(config.GetPAMConfig().Service, c.Username, c.PAMResponseHandler) 63 | } 64 | -------------------------------------------------------------------------------- /internal/pam/pam_test.go: -------------------------------------------------------------------------------- 1 | package pam 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestPAMStartFunc(t *testing.T) { 8 | _, err := pamStartFunc("", "", nil) 9 | if err != nil { 10 | t.Error(err) 11 | } 12 | } 13 | 14 | func TestPAMTxAuthenticate(t *testing.T) { 15 | // valid credential 16 | c := Credential{"user", "user1"} 17 | 18 | // valid service name 19 | service := "rmd" 20 | 21 | tx, err := pamStartFunc(service, c.Username, c.PAMResponseHandler) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | err = pamTxAuthenticate(tx) 27 | if err != nil { 28 | t.Error(err) 29 | } 30 | } 31 | 32 | func TestPAMAuthenticate(t *testing.T) { 33 | 34 | // Litmus test start func 35 | _, err := pamStartFunc("", "", nil) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | // Please use credentials different from those defined in Berkeley db for a consistent error message 41 | testCases := []struct { 42 | username string 43 | password string 44 | description string 45 | desiredResult string 46 | }{ 47 | {"user", "user1", "Valid Berkeley db user", ""}, 48 | {"x", "y", "Invalid Berkeley db user", "User not known to the underlying authentication module"}, 49 | {"user", "user", "Incorrect Berkeley db user", "Authentication failure"}, 50 | // Edit unix credentials here according to your testing platform 51 | // {"root", "s", "Valid unix user", ""}, 52 | {"a", "b", "Invalid unix user", "User not known to the underlying authentication module"}, 53 | {"root", "s1", "Incorrect unix user", "User not known to the underlying authentication module"}, 54 | } 55 | for _, testCase := range testCases { 56 | t.Run(testCase.description, func(t *testing.T) { 57 | c := Credential{testCase.username, testCase.password} 58 | err := c.PAMAuthenticate() 59 | if testCase.desiredResult == "" { 60 | if err != nil { 61 | t.Error(err) 62 | } 63 | } else { 64 | if err == nil { 65 | t.Error("No error detected as desired. Please check test inputs") 66 | } 67 | if err.Error() != testCase.desiredResult { 68 | t.Error(err) 69 | } 70 | } 71 | }) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /internal/pam/transaction.c: -------------------------------------------------------------------------------- 1 | // SPDX short identifier: BSD-2-Clause 2 | // Copyright 2011, krockot 3 | // Copyright 2015, Michael Steinert 4 | // All rights reserved. 5 | 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | 9 | // * Redistributions of source code must retain the above copyright notice, this 10 | // list of conditions and the following disclaimer. 11 | 12 | // * Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | #include "_cgo_export.h" 28 | #include 29 | #include 30 | #include 31 | 32 | int stringLength(char *str) { 33 | int i=0; 34 | while (str[i] != '\0') { 35 | i++; 36 | } 37 | return i; 38 | } 39 | 40 | int cb_pam_conv( 41 | int num_msg, 42 | const struct pam_message **msg, 43 | struct pam_response **resp, 44 | void *appdata_ptr) 45 | { 46 | *resp = (struct pam_response *) calloc(num_msg, sizeof **resp); 47 | if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG) { 48 | return PAM_CONV_ERR; 49 | } 50 | if (!*resp) { 51 | return PAM_BUF_ERR; 52 | } 53 | for (size_t i = 0; i < num_msg; ++i) { 54 | // cbPAMConv is a Go Wrapper to prompt the Go application for password. 55 | // return values are captured in cbConv_return as the Go Wrapper exports cbConv. 56 | struct cbConv_return result = cbConv( 57 | msg[i]->msg_style, 58 | (char *)msg[i]->msg, 59 | (long)appdata_ptr); 60 | if (result.r1 != PAM_SUCCESS) { 61 | for (size_t i = 0; i < num_msg; ++i) { 62 | if ((*resp)[i].resp) { 63 | memset((*resp)[i].resp, 0, stringLength((*resp)[i].resp)); 64 | free((*resp)[i].resp); 65 | } 66 | } 67 | memset(*resp, 0, num_msg * sizeof *resp); 68 | free(*resp); 69 | *resp = NULL; 70 | return PAM_CONV_ERR; 71 | } 72 | (*resp)[i].resp = result.r0; 73 | } 74 | return PAM_SUCCESS; 75 | } 76 | 77 | void init_pam_conv(struct pam_conv *conv, long c) 78 | { 79 | conv->conv = cb_pam_conv; 80 | conv->appdata_ptr = (void *) c; 81 | } 82 | -------------------------------------------------------------------------------- /internal/plugins/handler.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | logger "github.com/sirupsen/logrus" 8 | ) 9 | 10 | // Interfaces stores module-name to ModuleInterface mapping for all loaded plugins 11 | var Interfaces = make(map[string]ModuleInterface) 12 | 13 | // Enforce simplifies enforcing data using specified plugin 14 | // 15 | // Function checks if plugin is loaded, verifies if stored interface is not null and then calls Enforce() method of stored interface. 16 | // Returns error if any of steps above fails 17 | func Enforce(moduleName string, params map[string]interface{}) (string, error) { 18 | logger.Debugf("Enforce() requested for %v plugin", moduleName) 19 | iface, ok := Interfaces[moduleName] 20 | if !ok { 21 | return "", fmt.Errorf("Plugin '%v' is not loaded", moduleName) 22 | } 23 | if iface == nil { 24 | return "", errors.New("Internal error: nil pointer to plugin") 25 | } 26 | return iface.Enforce(params) 27 | } 28 | 29 | // Release simplifies releasing data using specified plugin 30 | // 31 | // Function checks if plugin is loaded, verifies if stored interface is not null and then calls Release() method of stored interface. 32 | // Returns error if any of steps above fails 33 | func Release(moduleName string, params map[string]interface{}) error { 34 | logger.Debugf("Release() requested for %v plugin", moduleName) 35 | iface, ok := Interfaces[moduleName] 36 | if !ok { 37 | return fmt.Errorf("Plugin '%v' is not loaded", moduleName) 38 | } 39 | if iface == nil { 40 | return errors.New("Internal error: nil pointer to plugin") 41 | } 42 | return iface.Release(params) 43 | } 44 | 45 | // Validate simplifies data validation for selected plugin 46 | // 47 | // Function checks if plugin is loaded, verifies if stored interface is not null and then calls Validate() method of stored interface. 48 | // Returns error if any of steps above fails 49 | func Validate(moduleName string, params map[string]interface{}) error { 50 | logger.Debugf("Validate() requested for %v plugin", moduleName) 51 | iface, ok := Interfaces[moduleName] 52 | if !ok { 53 | return fmt.Errorf("Plugin '%v' is not loaded", moduleName) 54 | } 55 | if iface == nil { 56 | return errors.New("Internal error: nil pointer to plugin") 57 | } 58 | return iface.Validate(params) 59 | } 60 | 61 | // Store stores provided module interface under provided name 62 | // 63 | // If module already exists or one of params is not correct (ex. nil interface) 64 | // error is returned 65 | func Store(name string, iface ModuleInterface) error { 66 | if name == "" || iface == nil { 67 | return errors.New("Invalid parameter") 68 | } 69 | 70 | _, ok := Interfaces[name] 71 | if ok { 72 | // module with this name already exists 73 | return errors.New("Module already registered under this name") 74 | } 75 | 76 | logger.Debugf("Plugin %v saved in loaded plugins", name) 77 | Interfaces[name] = iface 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /internal/plugins/handler_test.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/emicklei/go-restful" 8 | ) 9 | 10 | type FakeModule struct { 11 | } 12 | 13 | func (fm *FakeModule) Initialize(params map[string]interface{}) error { 14 | return nil 15 | } 16 | 17 | func (fm *FakeModule) GetEndpointPrefixes() []string { 18 | return []string{"ep1", "ep2"} 19 | } 20 | 21 | func (fm *FakeModule) HandleRequest(request *restful.Request, response *restful.Response) { 22 | } 23 | 24 | func (fm *FakeModule) Validate(params map[string]interface{}) error { 25 | if len(params) == 0 { 26 | return errors.New("No params") 27 | } 28 | return nil 29 | } 30 | 31 | func (fm *FakeModule) Enforce(params map[string]interface{}) (string, error) { 32 | if len(params) == 0 { 33 | return "", errors.New("No params") 34 | } 35 | return "111", nil 36 | } 37 | 38 | func (fm *FakeModule) Release(params map[string]interface{}) error { 39 | if len(params) == 0 { 40 | return errors.New("No params") 41 | } 42 | return nil 43 | } 44 | 45 | func (fm *FakeModule) GetCapabilities() string { 46 | return "" 47 | } 48 | 49 | var goodParams = map[string]interface{}{"param1": "text", "param2": 123} 50 | var badParams = map[string]interface{}{} 51 | 52 | func init() { 53 | var fm FakeModule 54 | Interfaces["mod1"] = nil 55 | Interfaces["mod2"] = &fm 56 | } 57 | 58 | func TestEnforce(t *testing.T) { 59 | type args struct { 60 | moduleName string 61 | params map[string]interface{} 62 | } 63 | tests := []struct { 64 | name string 65 | args args 66 | want string 67 | wantErr bool 68 | }{ 69 | {"Invalid module name and bad params", args{"modX", badParams}, "", true}, 70 | {"Invalid module and bad params", args{"mod1", badParams}, "", true}, 71 | {"Good module and bad params", args{"mod2", badParams}, "", true}, 72 | {"Good module and good params", args{"mod2", goodParams}, "111", false}, 73 | } 74 | for _, tt := range tests { 75 | t.Run(tt.name, func(t *testing.T) { 76 | got, err := Enforce(tt.args.moduleName, tt.args.params) 77 | if (err != nil) != tt.wantErr { 78 | t.Errorf("Enforce() error = %v, wantErr %v", err, tt.wantErr) 79 | return 80 | } 81 | if got != tt.want { 82 | t.Errorf("Enforce() = %v, want %v", got, tt.want) 83 | } 84 | }) 85 | } 86 | } 87 | 88 | func TestRelease(t *testing.T) { 89 | type args struct { 90 | moduleName string 91 | params map[string]interface{} 92 | } 93 | tests := []struct { 94 | name string 95 | args args 96 | wantErr bool 97 | }{ 98 | {"Invalid module name and bad params", args{"modX", badParams}, true}, 99 | {"Invalid module and bad params", args{"mod1", badParams}, true}, 100 | {"Good module and bad params", args{"mod2", badParams}, true}, 101 | {"Good module and good params", args{"mod2", goodParams}, false}, 102 | } 103 | for _, tt := range tests { 104 | t.Run(tt.name, func(t *testing.T) { 105 | if err := Release(tt.args.moduleName, tt.args.params); (err != nil) != tt.wantErr { 106 | t.Errorf("Release() error = %v, wantErr %v", err, tt.wantErr) 107 | } 108 | }) 109 | } 110 | } 111 | 112 | func TestValidate(t *testing.T) { 113 | type args struct { 114 | moduleName string 115 | params map[string]interface{} 116 | } 117 | tests := []struct { 118 | name string 119 | args args 120 | wantErr bool 121 | }{ 122 | {"Invalid module name and bad params", args{"modX", badParams}, true}, 123 | {"Invalid module and bad params", args{"mod1", badParams}, true}, 124 | {"Good module and bad params", args{"mod2", badParams}, true}, 125 | {"Good module and good params", args{"mod2", goodParams}, false}, 126 | } 127 | for _, tt := range tests { 128 | t.Run(tt.name, func(t *testing.T) { 129 | if err := Validate(tt.args.moduleName, tt.args.params); (err != nil) != tt.wantErr { 130 | t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) 131 | } 132 | }) 133 | } 134 | } 135 | 136 | func TestStore(t *testing.T) { 137 | type args struct { 138 | name string 139 | iface ModuleInterface 140 | } 141 | var fm FakeModule 142 | tests := []struct { 143 | name string 144 | args args 145 | wantErr bool 146 | }{ 147 | {"Empty name", args{"", &fm}, true}, 148 | {"NIL interface", args{"store1", nil}, true}, 149 | {"Correct case", args{"store2", &fm}, false}, 150 | {"Repeated name", args{"store2", &fm}, true}, 151 | } 152 | for _, tt := range tests { 153 | t.Run(tt.name, func(t *testing.T) { 154 | if err := Store(tt.args.name, tt.args.iface); (err != nil) != tt.wantErr { 155 | t.Errorf("Store() error = %v, wantErr %v", err, tt.wantErr) 156 | } 157 | }) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /internal/plugins/loader.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | "fmt" 5 | "plugin" 6 | 7 | util "github.com/intel/rmd/utils" 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | const ( 12 | symbolName = "Handle" 13 | ) 14 | 15 | // Load opens file given in path param and tries to load symbol "Handle" implementing ModuleInterface 16 | // Returns error if failed to open file, load symbol or cast interface 17 | func Load(path string) (ModuleInterface, error) { 18 | // additional verification if given path points to file 19 | isfile, err := util.IsRegularFile(path) 20 | if err != nil || !isfile { 21 | return nil, fmt.Errorf("Invalid plugin path %s", path) 22 | } 23 | 24 | plg, err := plugin.Open(path) 25 | if err != nil { 26 | return nil, fmt.Errorf("Failed to open plugin file %s: %s", path, err.Error()) 27 | } 28 | sym, err := plg.Lookup(symbolName) 29 | if err != nil { 30 | return nil, fmt.Errorf("Failed to load symbol %s: %s", symbolName, err.Error()) 31 | } 32 | 33 | result, ok := sym.(ModuleInterface) 34 | if !ok { 35 | return nil, fmt.Errorf("Symbol %s in not implementing ModuleInterface", symbolName) 36 | } 37 | 38 | return result, nil 39 | } 40 | 41 | // GetConfig reads configuration section given by name and returns it as a map of string-interface pairs 42 | func GetConfig(name string) (map[string]interface{}, error) { 43 | result := make(map[string]interface{}) 44 | err := viper.UnmarshalKey(name, &result) 45 | if err != nil { 46 | // Error is not related to lack of configuration 47 | return map[string]interface{}{}, fmt.Errorf("plugins.GetConfig(%s) failed: %s", name, err.Error()) 48 | } 49 | // viper is not informing about lack of section here so additional check needed 50 | if len(result) == 0 { 51 | return map[string]interface{}{}, fmt.Errorf("No config for %v found", name) 52 | } 53 | 54 | return result, nil 55 | } 56 | -------------------------------------------------------------------------------- /internal/plugins/modinterface.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | "github.com/emicklei/go-restful" 5 | ) 6 | 7 | // ModuleInterface defines interface of loadable RMD modules (dynamic loading with use of "plugin" package) 8 | type ModuleInterface interface { 9 | 10 | // Initialize is a module initialization function 11 | // config param contains all information needed to initialize plugin 12 | // (ex. path to config file) 13 | Initialize(params map[string]interface{}) error 14 | 15 | // GetEndpointPrefixes returns declaration of REST endpoints handled by this module 16 | // If function's implementation for specific module returns: 17 | // { "/endpoint1" and "/endpoint2/" } 18 | // then RMD will expose and forward to this module requests for URI's: 19 | // - http://ip:port/v1/endpoint1 20 | // - http://ip:port/v1/endpoint2/ 21 | // - all http://ip:port/v1/endpoint2/{something} 22 | GetEndpointPrefixes() []string 23 | 24 | // HandleRequest is called by HTTP request routing mechanism 25 | // 26 | // NOTE: currently "emicklei/go-restfull" package is used for HTTP requests routing 27 | // There's also a plan for standard HTTP package usage with following handling function 28 | // HandleRequest(wrt http.ResponseWriter, req *http.Request) error 29 | HandleRequest(request *restful.Request, response *restful.Response) 30 | 31 | // Validate allows workload module to check parameters before trying to enforce them 32 | Validate(params map[string]interface{}) error 33 | 34 | // Enforce allocates resources or set platform params according to data in 'params' map 35 | // Returned string should contain identifier for allocated resource. 36 | // If plugin does not need to store any identifier for future use in Release() then string should be empty 37 | Enforce(params map[string]interface{}) (string, error) 38 | 39 | // Release removes setting for given params 40 | // (in case of pstate it will be just disabling of monitoring for specified cores) 41 | Release(params map[string]interface{}) error 42 | 43 | // GetCapabilities returns comma separated list of platform resources used by plugin 44 | GetCapabilities() string 45 | } 46 | -------------------------------------------------------------------------------- /internal/proxy/client/client.go: -------------------------------------------------------------------------------- 1 | package proxyclient 2 | 3 | import ( 4 | "fmt" 5 | "net/rpc" 6 | 7 | "github.com/intel/rmd/internal/proxy/types" 8 | ) 9 | 10 | // Client is the connection to rpc server 11 | var Client *rpc.Client 12 | 13 | // ConnectRPCServer by a pipe pair 14 | // Be care about this method usage, it can only be called once while 15 | // we start RMD API server, sync.once could be one choice, developer 16 | // should control it. 17 | func ConnectRPCServer(in types.PipePair) error { 18 | Client = rpc.NewClient(&in) 19 | if Client == nil { 20 | return fmt.Errorf("Failed to connect rpc server") 21 | } 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /internal/proxy/client/generic.go: -------------------------------------------------------------------------------- 1 | package proxyclient 2 | 3 | // Enforce simplifies enforcing data in a more generic way 4 | func Enforce(module string, params map[string]interface{}) (string, error) { 5 | 6 | params["RMDMODULE"] = module 7 | var result string 8 | err := Client.Call("Proxy.Enforce", params, &result) 9 | if err != nil { 10 | return "", err 11 | } 12 | return result, nil 13 | } 14 | 15 | // Release simplifies releasing data in a more generic way 16 | func Release(module string, params map[string]interface{}) error { 17 | 18 | params["RMDMODULE"] = module 19 | err := Client.Call("Proxy.Release", params, nil) 20 | if err != nil { 21 | return err 22 | } 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /internal/proxy/client/pam.go: -------------------------------------------------------------------------------- 1 | package proxyclient 2 | 3 | import ( 4 | "github.com/intel/rmd/internal/proxy/types" 5 | ) 6 | 7 | // PAMAuthenticate leverage PAM to do authentication 8 | func PAMAuthenticate(user string, pass string) error { 9 | 10 | req := types.PAMRequest{ 11 | User: user, 12 | Pass: pass, 13 | } 14 | return Client.Call("Proxy.PAMAuthenticate", req, nil) 15 | } 16 | -------------------------------------------------------------------------------- /internal/proxy/client/resctrl.go: -------------------------------------------------------------------------------- 1 | package proxyclient 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/intel/rmd/internal/proxy/types" 7 | "github.com/intel/rmd/utils/resctrl" 8 | ) 9 | 10 | // GetResAssociation returns all resource group association 11 | func GetResAssociation(availableCLOS []string) map[string]*resctrl.ResAssociation { 12 | return resctrl.GetResAssociation(availableCLOS) 13 | } 14 | 15 | // GetRdtCosInfo returns RDT information 16 | func GetRdtCosInfo() map[string]*resctrl.RdtCosInfo { 17 | return resctrl.GetRdtCosInfo() 18 | } 19 | 20 | // IsIntelRdtMounted will check if resctrl mounted or not 21 | func IsIntelRdtMounted() bool { 22 | return resctrl.IsIntelRdtMounted() 23 | } 24 | 25 | // Commit resctrl.ResAssociation with given name 26 | func Commit(r *resctrl.ResAssociation, name string) error { 27 | // fmt.Println("CLient Side Commit : ", name, r) 28 | // TODO how to get error reason 29 | req := types.ResctrlRequest{ 30 | Name: name, 31 | Res: *r, 32 | } 33 | return Client.Call("Proxy.Commit", req, nil) 34 | } 35 | 36 | // DestroyResAssociation by resource group name 37 | func DestroyResAssociation(name string) error { 38 | // TODO how to get error reason 39 | // Add checking before using client and do reconnect 40 | return Client.Call("Proxy.DestroyResAssociation", name, nil) 41 | } 42 | 43 | // RemoveTasks moves tasks to default resource group 44 | func RemoveTasks(tasks []string) error { 45 | return Client.Call("Proxy.RemoveTasks", tasks, nil) 46 | } 47 | 48 | // RemoveCores moves cores to default resource group 49 | func RemoveCores(cores []string) error { 50 | return Client.Call("Proxy.RemoveCores", cores, nil) 51 | } 52 | 53 | // EnableCat enable cat feature on host 54 | func EnableCat() error { 55 | var result bool 56 | if err := Client.Call("Proxy.EnableCat", 0, &result); err != nil { 57 | return err 58 | } 59 | if result { 60 | return nil 61 | } 62 | return fmt.Errorf("Can not enable cat") 63 | } 64 | 65 | // ResetCOSParamsToDefaults resets L3 cache and MBA to default values for common COS# 66 | func ResetCOSParamsToDefaults(cosName string) error { 67 | // Call PQOS Wrapper 68 | return Client.Call("Proxy.ResetCOSParamsToDefaults", cosName, nil) 69 | } 70 | -------------------------------------------------------------------------------- /internal/proxy/server/generic.go: -------------------------------------------------------------------------------- 1 | package proxyserver 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/intel/rmd/internal/plugins" 7 | ) 8 | 9 | // Enforce simplifies enforcing data in a more generic way 10 | func (*Proxy) Enforce(params map[string]interface{}, id *string) error { 11 | 12 | rmdModuleAsInterface, ok := params["RMDMODULE"] 13 | if !ok { 14 | return errors.New("No RMD module specified") 15 | } 16 | 17 | rmdModuleAsString, ok := rmdModuleAsInterface.(string) 18 | if !ok { 19 | return errors.New("Failed to convert type for RMDMODULE") 20 | } 21 | 22 | // remove RMDMODULE element from map because 23 | // we don't want it to be passed to plugin 24 | delete(params, "RMDMODULE") 25 | 26 | var err error 27 | *id, err = plugins.Enforce(rmdModuleAsString, params) 28 | 29 | return err 30 | } 31 | 32 | // Release simplifies releasing data in a more generic way 33 | func (*Proxy) Release(params map[string]interface{}, unused *int) error { 34 | 35 | rmdModuleAsInterface, ok := params["RMDMODULE"] 36 | if !ok { 37 | return errors.New("No RMD module specified") 38 | } 39 | 40 | rmdModuleAsString, ok := rmdModuleAsInterface.(string) 41 | if !ok { 42 | return errors.New("Failed to convert type for RMDMODULE") 43 | } 44 | 45 | // remove RMDMODULE element from map because 46 | // we don't want it to be passed to plugin 47 | delete(params, "RMDMODULE") 48 | 49 | return plugins.Release(rmdModuleAsString, params) 50 | } 51 | -------------------------------------------------------------------------------- /internal/proxy/server/pam.go: -------------------------------------------------------------------------------- 1 | package proxyserver 2 | 3 | import ( 4 | "github.com/intel/rmd/internal/pam" 5 | "github.com/intel/rmd/internal/proxy/types" 6 | ) 7 | 8 | // PAMAuthenticate calls PAM authenticate 9 | func (*Proxy) PAMAuthenticate(request types.PAMRequest, dummy *int) error { 10 | 11 | c := pam.Credential{ 12 | Username: request.User, 13 | Password: request.Pass, 14 | } 15 | 16 | return c.PAMAuthenticate() 17 | 18 | } 19 | -------------------------------------------------------------------------------- /internal/proxy/server/resctrl.go: -------------------------------------------------------------------------------- 1 | package proxyserver 2 | 3 | import ( 4 | "github.com/intel/rmd/internal/proxy/types" 5 | "github.com/intel/rmd/utils/pqos" 6 | "github.com/intel/rmd/utils/resctrl" 7 | ) 8 | 9 | // Commit resource group 10 | func (*Proxy) Commit(r types.ResctrlRequest, dummy *int) error { 11 | // return resctrl.Commit(&r.Res, r.Name) 12 | // Call PQOS Wrapper 13 | pqos.AllocateCLOS(&r.Res, r.Name) 14 | return nil 15 | } 16 | 17 | // DestroyResAssociation remove resource association 18 | func (*Proxy) DestroyResAssociation(grpName string, dummy *int) error { 19 | return resctrl.DestroyResAssociation(grpName) 20 | } 21 | 22 | // RemoveTasks move tasks to default group 23 | func (*Proxy) RemoveTasks(tasks []string, dummy *int) error { 24 | // Call PQOS Wrapper 25 | return pqos.DeallocateTasks(tasks) 26 | } 27 | 28 | // RemoveCores move tasks to default group 29 | func (*Proxy) RemoveCores(cores []string, dummy *int) error { 30 | // Call PQOS Wrapper 31 | return pqos.DeallocateCores(cores) 32 | } 33 | 34 | // EnableCat mounts resctrl 35 | func (*Proxy) EnableCat(dummy *int, result *bool) error { 36 | *result = resctrl.EnableCat() 37 | return nil 38 | } 39 | 40 | // ResetCOSParamsToDefaults resets L3 cache and MBA to default values for common COS# 41 | func (*Proxy) ResetCOSParamsToDefaults(cosName string, dummy *int) error { 42 | // Call PQOS Wrapper 43 | return pqos.ResetCOSParamsToDefaults(cosName) 44 | } 45 | -------------------------------------------------------------------------------- /internal/proxy/server/server.go: -------------------------------------------------------------------------------- 1 | package proxyserver 2 | 3 | import ( 4 | "fmt" 5 | "net/rpc" 6 | 7 | "github.com/intel/rmd/internal/proxy/types" 8 | ) 9 | 10 | // Proxy struct of to rpc server 11 | type Proxy struct { 12 | } 13 | 14 | // RegisterAndServe to register rpc and serve 15 | func RegisterAndServe(pipes types.PipePair) { 16 | err := rpc.Register(new(Proxy)) 17 | if err != nil { 18 | fmt.Println(err) 19 | } 20 | rpc.ServeConn(&pipes) 21 | } 22 | -------------------------------------------------------------------------------- /internal/proxy/types/pam.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // PAMRequest is request from rpc client 4 | type PAMRequest struct { 5 | User string 6 | Pass string 7 | } 8 | -------------------------------------------------------------------------------- /internal/proxy/types/pipepair.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // PipePair is a pair of pipe 8 | type PipePair struct { 9 | Reader *os.File 10 | Writer *os.File 11 | } 12 | 13 | // Read from pipe pair's reader 14 | func (pp *PipePair) Read(p []byte) (int, error) { 15 | return pp.Reader.Read(p) 16 | } 17 | 18 | // Write to pipe pair's writer 19 | func (pp *PipePair) Write(p []byte) (int, error) { 20 | return pp.Writer.Write(p) 21 | } 22 | 23 | // Close the pipe pair 24 | func (pp *PipePair) Close() error { 25 | pp.Writer.Close() 26 | return pp.Reader.Close() 27 | } 28 | -------------------------------------------------------------------------------- /internal/proxy/types/resctrl.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/intel/rmd/utils/resctrl" 5 | ) 6 | 7 | // ResctrlRequest struct of to rpc server 8 | type ResctrlRequest struct { 9 | Name string 10 | Res resctrl.ResAssociation 11 | } 12 | -------------------------------------------------------------------------------- /modules/cache/cachepool.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "sync" 7 | 8 | "github.com/intel/rmd/modules/cache/config" 9 | util "github.com/intel/rmd/utils/bitmap" 10 | ) 11 | 12 | var cachePoolReserved = make(map[string]*Reserved, 0) 13 | var cachePoolOnce sync.Once 14 | 15 | // helper function to get Reserved resource 16 | func getReservedCache( 17 | wayCandidate int, 18 | wayOffset, osCacheWays uint, 19 | osCPUbm *util.Bitmap, 20 | sysc map[string]SysCache) (*Reserved, error) { 21 | 22 | r := &Reserved{} 23 | 24 | schemata := map[string]*util.Bitmap{} 25 | osCPUs := map[string]*util.Bitmap{} 26 | var err error 27 | 28 | for _, sc := range sysc { 29 | wc := wayCandidate 30 | bm, _ := BitmapsCPUWrapper([]string{sc.SharedCPUList}) 31 | osCPUs[sc.ID] = osCPUbm.And(bm) 32 | // no os group on this cache id 33 | if !osCPUs[sc.ID].IsEmpty() { 34 | wc = wc << osCacheWays 35 | } 36 | wc = wc << wayOffset 37 | mask := strconv.FormatUint(uint64(wc), 16) 38 | schemata[sc.ID], err = BitmapsCacheWrapper(mask) 39 | if err != nil { 40 | return r, err 41 | } 42 | } 43 | 44 | r.Schemata = schemata 45 | return r, nil 46 | } 47 | 48 | // GetCachePoolLayout returns cache pool layout based on configuration 49 | func GetCachePoolLayout() (map[string]*Reserved, error) { 50 | var returnErr error 51 | cachePoolOnce.Do(func() { 52 | poolConf := config.NewCachePoolConfig() 53 | osConf := config.NewOSConfig() 54 | ways := GetCosInfo().CbmMaskLen 55 | 56 | if osConf.CacheWays+poolConf.Guarantee+poolConf.Besteffort+poolConf.Shared > uint(ways) { 57 | returnErr = fmt.Errorf( 58 | "Error config: Guarantee + Besteffort + Shared + OS reserved ways should be less or equal to %d", ways) 59 | return 60 | } 61 | 62 | // set layout for cache pool 63 | level := GetLLC() 64 | syscaches, err := GetSysCaches(int(level)) 65 | osCPUbm, err := BitmapsCPUWrapper([]string{osConf.CPUSet}) 66 | 67 | if err != nil { 68 | returnErr = err 69 | return 70 | } 71 | 72 | if poolConf.Guarantee > 0 { 73 | wc := 1< 0 { 87 | wc := 1< 0 { 103 | wc := 1< 0 { 33 | infraCPUbm, err = BitmapsCPUWrapper([]string{infraconf.CPUSet}) 34 | if err != nil { 35 | returnErr = err 36 | return 37 | } 38 | } else { 39 | infraCPUbm, _ = BitmapsCPUWrapper("Ox0") 40 | } 41 | 42 | level := GetLLC() 43 | syscaches, err := GetSysCaches(int(level)) 44 | if err != nil { 45 | returnErr = err 46 | return 47 | } 48 | 49 | isocpu := cpu.IsolatedCPUs() 50 | 51 | if isocpu != "" { 52 | isolatedCPUbm, _ = BitmapsCPUWrapper([]string{isocpu}) 53 | } else { 54 | isolatedCPUbm, _ = BitmapsCPUWrapper("Ox0") 55 | } 56 | 57 | for _, sc := range syscaches { 58 | bm, _ := BitmapsCPUWrapper([]string{sc.SharedCPUList}) 59 | cpuPoolPerCache["all"][sc.ID] = bm.Axor(osCPUbm).Axor(infraCPUbm) 60 | cpuPoolPerCache["isolated"][sc.ID] = bm.Axor(osCPUbm).Axor(infraCPUbm).And(isolatedCPUbm) 61 | } 62 | }) 63 | return cpuPoolPerCache, returnErr 64 | } 65 | -------------------------------------------------------------------------------- /modules/cache/osgroup.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | "sync" 8 | 9 | proxyclient "github.com/intel/rmd/internal/proxy/client" 10 | "github.com/intel/rmd/modules/cache/config" 11 | util "github.com/intel/rmd/utils/bitmap" 12 | "github.com/intel/rmd/utils/pqos" 13 | "github.com/intel/rmd/utils/proc" 14 | ) 15 | 16 | var osGroupReserve = &Reserved{} 17 | var osOnce sync.Once 18 | 19 | // GetOSGroupReserve returns os reserved resource group 20 | func GetOSGroupReserve() (Reserved, error) { 21 | var returnErr error 22 | osOnce.Do(func() { 23 | conf := config.NewOSConfig() 24 | osCPUbm, err := BitmapsCPUWrapper([]string{conf.CPUSet}) 25 | if err != nil { 26 | returnErr = err 27 | return 28 | } 29 | osGroupReserve.AllCPUs = osCPUbm 30 | 31 | level := GetLLC() 32 | syscaches, err := GetSysCaches(int(level)) 33 | if err != nil { 34 | returnErr = err 35 | return 36 | } 37 | 38 | // We though the ways number are same on all caches ID 39 | // FIXME if exception, fix it. 40 | ways, _ := strconv.Atoi(syscaches["0"].WaysOfAssociativity) 41 | if conf.CacheWays > uint(ways) { 42 | returnErr = fmt.Errorf("The request OSGroup cache ways %d is larger than available %d", 43 | conf.CacheWays, ways) 44 | return 45 | } 46 | 47 | schemata := map[string]*util.Bitmap{} 48 | osCPUs := map[string]*util.Bitmap{} 49 | 50 | for _, sc := range syscaches { 51 | bm, _ := BitmapsCPUWrapper([]string{sc.SharedCPUList}) 52 | osCPUs[sc.ID] = osCPUbm.And(bm) 53 | if osCPUs[sc.ID].IsEmpty() { 54 | schemata[sc.ID], returnErr = BitmapsCacheWrapper("0") 55 | if returnErr != nil { 56 | return 57 | } 58 | } else { 59 | mask := strconv.FormatUint(1< strconv.Itoa(MaxMBAPercentage) { 95 | proc.SetMbaMbpsMode(true) 96 | } else { 97 | proc.SetMbaMbpsMode(false) 98 | } 99 | } 100 | delete(osGroup.CacheSchemata, "MB") 101 | } else { 102 | proc.SetMbaMbpsMode(false) 103 | } 104 | originBM, err := BitmapsCPUWrapper(osGroup.CPUs) 105 | if err != nil { 106 | return err 107 | } 108 | 109 | // NOTE , simpleness, brutal. Stolen CPUs from other groups. 110 | newBM := originBM.Or(reserve.AllCPUs) 111 | osGroup.CPUs = newBM.ToString() 112 | 113 | level := GetLLC() 114 | cacheLevel := "L" + strconv.FormatUint(uint64(level), 10) 115 | schemata, err := GetAvailableCacheSchemata(allres, []string{pqos.InfraGoupCOS, pqos.OSGroupCOS}, "none", cacheLevel) 116 | 117 | if err != nil { 118 | return err 119 | } 120 | 121 | for i, v := range osGroup.CacheSchemata[cacheLevel] { 122 | cacheID := strconv.Itoa(int(v.ID)) 123 | // OSGroup is the first Group, use the edge cache ways. 124 | // FIXME , left or right cache ways, need to be check. 125 | conf := config.NewOSConfig() 126 | request, _ := BitmapsCacheWrapper(strconv.FormatUint(1< min_cache, allocate 26 | // from this pool. 27 | // Optional. 28 | // SHARED: shared group, it's a resource group instead of a pool. When try 29 | // to allocate max_cache == min_cache == 0, just add cpus, tasks IDs 30 | // to this resource group. Need to count how many workload in this 31 | // resource group when calculating hospitality score. 32 | // Optional 33 | 34 | const ( 35 | // OS is os resource group name 36 | OS = "os" 37 | // Infra is infra resource group name 38 | Infra = "infra" 39 | // Guarantee is guarantee resource group name 40 | Guarantee = "guarantee" 41 | // Besteffort is besteffort resource group name 42 | Besteffort = "besteffort" 43 | // Shared is shared resource group name 44 | Shared = "shared" 45 | ) 46 | 47 | // ReservedInfo is all reserved resource group information 48 | var ReservedInfo map[string]*Reserved 49 | var revinfoOnce sync.Once 50 | 51 | // GetReservedInfo returns all reserved information 52 | func GetReservedInfo() map[string]*Reserved { 53 | 54 | revinfoOnce.Do(func() { 55 | ReservedInfo = make(map[string]*Reserved, 10) 56 | 57 | r, err := GetOSGroupReserve() 58 | if err == nil { 59 | ReservedInfo[OS] = &r 60 | } 61 | 62 | fr, err := GetInfraGroupReserve() 63 | if err == nil { 64 | ReservedInfo[Infra] = &fr 65 | } 66 | 67 | poolinfo, err := GetCachePoolLayout() 68 | if err == nil { 69 | for k, v := range poolinfo { 70 | ReservedInfo[k] = v 71 | } 72 | } 73 | }) 74 | 75 | return ReservedInfo 76 | } 77 | 78 | // GetAvailableCacheSchemata returns available schemata of caches from 79 | // specific pool: guarantee, besteffort, shared or just none 80 | func GetAvailableCacheSchemata(allres map[string]*resctrl.ResAssociation, 81 | ignoreGroups []string, 82 | pool string, 83 | cacheLevel string) (map[string]*libutil.Bitmap, error) { 84 | 85 | GetReservedInfo() 86 | // FIXME A central util to generate schemata Bitmap 87 | schemata := map[string]*libutil.Bitmap{} 88 | 89 | if len(allres) >= GetCosInfo().NumClosids { 90 | return nil, fmt.Errorf("error, not enough CLOS on host, %d used", len(allres)) 91 | } 92 | 93 | if pool == "none" { 94 | for k := range ReservedInfo[OS].Schemata { 95 | schemata[k], _ = BitmapsCacheWrapper(GetCosInfo().CbmMask) 96 | } 97 | } else { 98 | resv, ok := ReservedInfo[pool] 99 | if !ok { 100 | return nil, fmt.Errorf("error doesn't support pool %s", pool) 101 | } 102 | 103 | for k, v := range resv.Schemata { 104 | schemata[k] = v 105 | } 106 | } 107 | 108 | for k, v := range allres { 109 | if util.HasElem(ignoreGroups, k) { 110 | continue 111 | } 112 | if sv, ok := v.CacheSchemata[cacheLevel]; ok { 113 | if len(schemata) <= 0 { 114 | // skip rest of iteration execution 115 | // (without data in schemata[] there will be failure below) 116 | continue 117 | } 118 | for _, cv := range sv { 119 | k := strconv.Itoa(int(cv.ID)) 120 | bm, _ := BitmapsCacheWrapper(cv.Mask) 121 | // And check cpu list is empty 122 | if cv.Mask == GetCosInfo().CbmMask { 123 | continue 124 | } 125 | schemata[k] = schemata[k].Axor(bm) 126 | } 127 | } 128 | } 129 | return schemata, nil 130 | } 131 | 132 | // GetCachePoolName will return pool name based on MaxCache, MinCache 133 | func GetCachePoolName(MaxWays, MinWays uint32) (string, error) { 134 | if MaxWays == 0 { 135 | return Shared, nil 136 | } else if MaxWays > MinWays && MinWays != 0 { 137 | return Besteffort, nil 138 | } else if MaxWays == MinWays { 139 | return Guarantee, nil 140 | } 141 | 142 | return "", fmt.Errorf("max_cache=%d, min_cache=%d, doesn't support", MaxWays, MinWays) 143 | } 144 | -------------------------------------------------------------------------------- /modules/hospitality/hospitality_api.go: -------------------------------------------------------------------------------- 1 | package hospitality 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/emicklei/go-restful" 7 | log "github.com/sirupsen/logrus" 8 | 9 | rmderror "github.com/intel/rmd/internal/error" 10 | ) 11 | 12 | // Register add handlers for /v1/hospitality endpoint 13 | func Register(prefix string, container *restful.Container) { 14 | ws := new(restful.WebService) 15 | ws. 16 | Path(prefix + "hospitality"). 17 | Doc("Show the hospitality information of a host"). 18 | Consumes(restful.MIME_JSON). 19 | Produces(restful.MIME_JSON) 20 | 21 | ws.Route(ws.POST("/").To(GetByRequest). 22 | Doc("Get the hospitality information per request."). 23 | Operation("GetByRequest")) 24 | 25 | container.Add(ws) 26 | } 27 | 28 | // GetByRequest returns hospitality score by request 29 | func GetByRequest(request *restful.Request, response *restful.Response) { 30 | hr := &Request{} 31 | err := request.ReadEntity(&hr) 32 | 33 | if err != nil { 34 | response.AddHeader("Content-Type", "text/plain") 35 | response.WriteErrorString(http.StatusInternalServerError, err.Error()) 36 | return 37 | } 38 | 39 | log.Infof("Try to get hospitality score by %v", hr) 40 | score := &Hospitality{} 41 | err = score.GetByRequest(hr) 42 | if err != nil { 43 | apperr, ok := err.(rmderror.AppError) 44 | if ok { 45 | response.WriteErrorString(apperr.Code, apperr.Error()) 46 | } else { 47 | response.WriteErrorString(http.StatusInternalServerError, err.Error()) 48 | } 49 | return 50 | } 51 | response.WriteEntity(score) 52 | } 53 | -------------------------------------------------------------------------------- /modules/hospitality/hospitality_test.go: -------------------------------------------------------------------------------- 1 | package hospitality 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestGetByRequest(t *testing.T) { 10 | Convey("Test get hospitality score by request", t, func(c C) { 11 | h := &Hospitality{} 12 | req := &Request{} 13 | 14 | c.Convey("Test get hospitality score bad request min=0 ", func(c C) { 15 | req.MaxCache = 1 16 | req.MinCache = 0 17 | err := h.GetByRequest(req) 18 | c.So(err, ShouldNotBeNil) 19 | }) 20 | c.Convey("Test get hospitality score bad request max=0", func(c C) { 21 | req.MaxCache = 0 22 | req.MinCache = 1 23 | err := h.GetByRequest(req) 24 | c.So(err, ShouldNotBeNil) 25 | }) 26 | c.Convey("Test get hospitality score bad request max " 8 | exit 1 9 | fi 10 | 11 | DOWNLOAD_PATH=$1 12 | OUTPUT_PATH=$2 13 | 14 | echo "Downloading all dependencies to: $DOWNLOAD_PATH" 15 | 16 | # create output directory if does not exists 17 | mkdir -p $DOWNLOAD_PATH 18 | 19 | # set GOPATH to download directory 20 | export GOPATH=$DOWNLOAD_PATH 21 | # force to enable Go modules (just in case) 22 | export GO111MODULE=on 23 | 24 | 25 | # check if current directory or parent directory contains go.mod 26 | INPUT="" 27 | 28 | if [ -f go.mod ] 29 | then 30 | INPUT="./go.mod" 31 | elif [ -f ../go.mod] 32 | then 33 | INPUT="../go.mod" 34 | else 35 | echo "No go.mod found in current neither parent directory" 36 | exit 1 37 | fi 38 | 39 | echo "Reading packages from: $INPUT" 40 | 41 | for line in `grep "^\s\+" $INPUT | sed -e "s/\s\+\(\S\+\) \(\S\+\).*/\1@\2/"` 42 | do 43 | echo "... fetching $line" 44 | go get $line 45 | done 46 | 47 | # All packages downloaded - create tarbal 48 | 49 | START_POINT=`pwd` 50 | # enter parent folder of download path 51 | cd $(dirname $DOWNLOAD_PATH) 52 | # create a .tar.gz file 53 | tar czf $OUTPUT_PATH/$PACKAGE_NAME $(basename $DOWNLOAD_PATH) 54 | 55 | echo Done 56 | # show the tarball details (ex. human readable size) 57 | ls -lh $OUTPUT_PATH/$PACKAGE_NAME 58 | 59 | # go back to the directory where has been launched 60 | cd $START_POINT 61 | 62 | # remove downloaded packages (first change dir modes as go get set them to RO) 63 | chmod -R u+rw $DOWNLOAD_PATH 64 | rm -r $DOWNLOAD_PATH 65 | 66 | -------------------------------------------------------------------------------- /rpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rmd", 3 | "maintainer": "intel-rmd ", 4 | "summary": "Resource management daemon", 5 | "description": "Resource Management Daemon (RMD) is a system daemon running on generic Linux platforms. The purpose of this daemon is to provide a central uniform interface portal for hardware resource management tasks on x86 platforms.", 6 | "license": "Apache-2.0", 7 | "url": "https://github.com/intel/rmd", 8 | "files": [ 9 | { 10 | "from": "build/linux/x86_64/!name!", 11 | "to": "/usr/local/sbin/", 12 | "base": "build/linux", 13 | "fperm": "0755" 14 | }, 15 | { 16 | "from": "etc/**/**", 17 | "to": "/usr/local" 18 | }, 19 | { 20 | "from": "packaging/rmd.service", 21 | "to": "/lib/systemd/system/", 22 | "base": "packaging" 23 | } 24 | ], 25 | "requires": [ 26 | "openssl", 27 | "pam-devel", 28 | "db4-utils" 29 | ], 30 | "postinst": "packaging/postinst.sh", 31 | "prerm": "packaging/prerm.sh", 32 | "postrm": "packaging/postrm.sh" 33 | } 34 | -------------------------------------------------------------------------------- /scripts/build-opts-get: -------------------------------------------------------------------------------- 1 | # Helper script for setting correct Go build tags based on make options 2 | 3 | while getopts ":b:" opt; do 4 | case ${opt} in 5 | b) 6 | if [ "$OPTARG" != "standard" ]; 7 | then 8 | export GOBUILDOPTS="-tags $OPTARG" 9 | fi 10 | ;; 11 | *) 12 | ;; 13 | esac 14 | done 15 | 16 | if [ ! -z "$GOBUILDOPTS" ]; 17 | then 18 | echo "Non-standard build tags used: $GOBUILDOPTS" 19 | fi -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # build rmd 3 | 4 | BASE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 5 | source $BASE/go-env 6 | __proj_dir="$(dirname "$__dir")" 7 | __repo_path="github.com/intel/rmd" 8 | BUILD_DATE=${BUILD_DATE:-$( date +%Y%m%d-%H:%M:%S )} 9 | 10 | # Use version number hardcoded in file 11 | version=$(cat ./RMD_VERSION) 12 | if [ -d ./.git ]; 13 | then 14 | # Building from git working directory 15 | # Use data from repo (ex. add build number and commit id) 16 | version+=$(git describe --tags --dirty --abbrev=14 | cut -f"3,4" -d"-" | sed -E 's/^g/+/' ) 17 | revision=$(git rev-parse --short HEAD 2> /dev/null || echo 'unknown' ) 18 | branch=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo 'unknown' ) 19 | else 20 | # Building from tarball/zip 21 | revision="" 22 | branch="" 23 | fi 24 | 25 | 26 | go_version=$( go version | sed -e 's/^[^0-9.]*\([0-9.]*\).*/\1/' ) 27 | 28 | GO_MINOR_VERSION=$( go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2) 29 | 30 | if [[ ${GO_MINOR_VERSION} -ge 11 ]]; then 31 | export GO111MODULE=on 32 | fi 33 | 34 | if [ -d ./.git ]; 35 | then 36 | echo "git commit: $(git log --pretty=format:"%H" -1)" 37 | else 38 | echo "building from tarbal" 39 | fi 40 | 41 | # rebuild binaries: 42 | export GOOS=${GOOS:-$(go env GOOS)} 43 | export GOARCH=${GOARCH:-$(go env GOARCH)} 44 | 45 | if [[ "${GOARCH}" == "amd64" ]]; then 46 | build_path="${__proj_dir}/build/${GOOS}/x86_64" 47 | else 48 | build_path="${__proj_dir}/build/${GOOS}/${GOARCH}" 49 | fi 50 | 51 | mkdir -p "${build_path}" 52 | echo "building rmd for ${GOOS}/${GOARCH}" 53 | 54 | ldflags=" 55 | -X ${__repo_path}/version.Version=${version} 56 | -X ${__repo_path}/version.Revision=${revision} 57 | -X ${__repo_path}/version.Branch=${branch} 58 | -X ${__repo_path}/version.BuildDate=${BUILD_DATE} 59 | -X ${__repo_path}/version.GoVersion=${go_version}" 60 | 61 | # load code for setting GOBUILDOPTS variable based on command line params 62 | source $BASE/build-opts-get 63 | 64 | go build -i -v $GOBUILDOPTS -ldflags "$ldflags" -o ${build_path}/rmd ./cmd/rmd || exit 1 65 | go build -i -v $GOBUILDOPTS -ldflags=-linkmode=external -o ${build_path}/gen_conf $BASE/../cmd/gen_conf/gen_conf.go || exit 1 66 | -------------------------------------------------------------------------------- /scripts/deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | BASE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 4 | . $BASE/go-env 5 | -------------------------------------------------------------------------------- /scripts/go-env: -------------------------------------------------------------------------------- 1 | GOROOT=`go env |grep "GOROOT" |cut -d "=" -f2` 2 | GOROOT=${GOROOT#\"} 3 | GOROOT=${GOROOT%\"} 4 | 5 | GOPATH=`go env |grep GOPATH |cut -d "=" -f 2` 6 | GOPATH=${GOPATH%\"} 7 | GOPATH=${GOPATH#\"} 8 | 9 | export GOROOT 10 | export GOPATH 11 | export PATH=${PATH}:${GOROOT}/bin:${GOPATH}/bin 12 | -------------------------------------------------------------------------------- /scripts/hacking.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Author: Eli Qiao 3 | BASE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 4 | ret=$? 5 | # hacking command list 6 | # don't check shift 7 | cmds=("golint" "go fmt" "go tool vet -shift=false") 8 | 9 | function hack { 10 | local cmd=$1 11 | local file=$2 12 | local rev 13 | 14 | rev=$($cmd "$file") 15 | 16 | if [[ $? -ne 0 ]] || [[ ! -z $rev ]]; then 17 | if [[ ! -z $rev ]]; then 18 | echo -e "\033[1;91m${rev} \033[0m" 19 | else 20 | # FIXME go tool vet does not give output 21 | echo -e "\033[1;91mgo tool vet $file failed \033[0m" 22 | fi 23 | else 24 | echo "0" 25 | fi 26 | } 27 | 28 | function do_check { 29 | local f=$1 30 | local rev 31 | if [ -f "${f}" ]; then 32 | for ((i = 0; i < ${#cmds[@]}; i++)) do 33 | 34 | rev=$(hack "${cmds[$i]}" $f) 35 | 36 | if [[ ! -z ${rev} && ${rev} != "0" ]]; then 37 | echo $rev 38 | RET=-1 39 | fi 40 | done 41 | fi 42 | } 43 | 44 | # global variable for checking result 45 | RET=0 46 | 47 | if [ $# -eq 1 ] && [ "$1" == "-f" ]; then 48 | echo "do full code checking ..." 49 | find $BASE/../ | grep -v vendor | grep -v "\.git" | grep -v test |grep "\.go" > tmp 50 | while IFS='' read -r line || [[ -n "$line" ]]; do 51 | do_check $line 52 | done < "tmp" 53 | # echo "Total code lines:" 54 | # wc -l `find ./ | grep -v vendor | grep -v .git | grep -v test |grep ".go"` 55 | 56 | rm "tmp" 57 | else 58 | files=$(git diff HEAD --stat | awk '{if ($1 ~ /\.go$/) {print $1}}') 59 | arr=($files) 60 | for f in "${arr[@]}" 61 | do 62 | do_check $f 63 | done 64 | fi 65 | 66 | if [[ $RET -ne 0 ]]; then 67 | echo ":( <<< Please address coding style mistakes." 68 | else 69 | echo ":) >>> No errors for coding style" 70 | fi 71 | 72 | exit $RET 73 | -------------------------------------------------------------------------------- /scripts/hacking_v2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | function hacking() { 3 | BASE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 4 | cd $BASE/.. 5 | echo $BASE 6 | echo " " 7 | set -x 8 | LINTOUT=$(golint ./...) 9 | set +x 10 | RETGOLINT=0 11 | if [[ ! -z $LINTOUT ]]; then 12 | # echo " <<< golint errors: " 13 | # echo -e ${LINTOUT} 14 | # echo " <<< " 15 | echo ":( <<< Please address golint errors." 16 | RETGOLINT=1 17 | else 18 | echo ":) >>> No errors from golint" 19 | fi 20 | 21 | echo " " 22 | set -x 23 | FMTOUT=$(gofmt -l ./) 24 | set +x 25 | RETGOFMT=0 26 | if [[ ! -z $FMTOUT ]]; then 27 | echo " <<< gofmt: files that have formatting issues: " 28 | gofmt -d ./ 29 | echo " <<< " 30 | echo ":( <<< Please address gofmt errors." 31 | RETGOFMT=1 32 | else 33 | echo ":) >>> No errors from gofmt" 34 | fi 35 | 36 | echo " " 37 | set -x 38 | VETOUT=$(go vet ./...) 39 | set +x 40 | RETGOVET=0 41 | if [[ $? -ne 0 ]]; then 42 | echo " <<< go vet errors: " 43 | echo -e ${VETOUT} 44 | echo " <<< " 45 | echo ":( <<< Please address go vet errors." 46 | RETGOVET=1 47 | else 48 | echo ":) >>> No errors from go vet" 49 | fi 50 | 51 | echo " " 52 | echo " >>> Done checking" 53 | echo " " 54 | 55 | RET=0 56 | if [[ $RETGOLINT -ne 0 ]] || [[ $RETGOFMT -ne 0 ]] || [[ $RETGOVET -ne 0 ]]; then 57 | RET=1 58 | fi 59 | echo "return $RET" 60 | return $RET 61 | } 62 | 63 | hacking 64 | 65 | exit $? 66 | 67 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | GO_MINOR_VERSION=$(go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2) 3 | 4 | if [ ${GO_MINOR_VERSION} -lt 11 ]; then 5 | echo "unsupported go version. require >= go1.11" 6 | exit 1 7 | fi 8 | 9 | export GO111MODULE=on 10 | BASE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 11 | source $BASE/go-env 12 | #Copy only for git version. For package, this will be done from spec file 13 | if [ -d $BASE/../.git ]; 14 | then 15 | cp -r $BASE/../etc/rmd /etc 16 | fi 17 | 18 | USER="rmd" 19 | useradd $USER || echo "User rmd already exists." 20 | 21 | LOGFILE="/var/log/rmd/rmd.log" 22 | if [ ! -d ${LOGFILE%/*} ]; then 23 | mkdir -p ${LOGFILE%/*} 24 | chown $USER:$USER ${LOGFILE%/*} 25 | fi 26 | 27 | DBFILE="/var/run/rmd/rmd.db" 28 | if [ ! -d ${DBFILE%/*} ]; then 29 | mkdir -p ${DBFILE%/*} 30 | chown $USER:$USER ${DBFILE%/*} 31 | fi 32 | 33 | 34 | if [ "$1" == "--skip-pam-userdb" ]; then 35 | $BASE/setup_pam_files.sh $1 36 | else 37 | $BASE/setup_pam_files.sh 38 | fi 39 | 40 | DATA="\"logfile\":\"$LOGFILE\", \"dbtransport\":\"$DBFILE\", \"logtostdout\":false" 41 | gen_conf -path /etc/rmd/rmd.toml -data "{$DATA}" 42 | 43 | if [ -f "/lib/systemd/system/rmd.service" ]; then 44 | systemctl daemon-reload 45 | systemctl enable rmd.service 46 | systemctl start rmd.service 47 | fi -------------------------------------------------------------------------------- /scripts/prerm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | if [ -f "/lib/systemd/system/rmd.service" ]; then 4 | systemctl stop rmd.service 5 | systemctl disable rmd.service 6 | fi 7 | -------------------------------------------------------------------------------- /scripts/rmd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=rmd 3 | 4 | [Service] 5 | User=root 6 | Group=root 7 | ExecStart=/usr/bin/rmd 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /scripts/setup_pam_files.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | BASE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 3 | PAMSRCFILE="/etc/rmd/pam/rmd" 4 | PAMDIR="/etc/pam.d" 5 | if [ -d $PAMDIR ]; then 6 | cp $PAMSRCFILE $PAMDIR 7 | fi 8 | 9 | BASE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 10 | BERKELEYDBFILE="/etc/rmd/pam/rmd_users.db" 11 | 12 | function SetupRMDUsersByResponse { 13 | if [ $1 == "y" -o $1 == "Y" ]; then 14 | $BASE/setup_rmd_users.sh 15 | elif [ $1 != "n" -a $1 != "N" ]; then 16 | echo "Invalid input. No action taken." 17 | fi 18 | } 19 | 20 | if [ "$1" == "--skip-pam-userdb" ]; then 21 | SetupRMDUsersByResponse 'n' 22 | exit 0 23 | fi 24 | 25 | if [ -f $BERKELEYDBFILE ]; then 26 | echo "Do you want to create/update users in RMD Berkeley DB file?(y/n)" 27 | read -r a 28 | SetupRMDUsersByResponse $a 29 | else 30 | SetupRMDUsersByResponse 'y' 31 | fi 32 | -------------------------------------------------------------------------------- /scripts/setup_rmd_users.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ $EUID != 0 ]]; then 4 | echo "This script can be run only by root" 5 | exit 1 6 | fi 7 | 8 | BERKELEYDBDIR="/etc/rmd/pam" 9 | BERKELEYDBFILENAME="rmd_users.db" 10 | echo 'Setup Berkeley db users' 11 | while true 12 | do 13 | echo 'Enter username or 0 to stop' 14 | read u 15 | if [ $u == "0" ]; then 16 | break 17 | fi 18 | echo $u >> users 19 | echo 'Enter password:' 20 | read -s p 21 | openssl passwd -crypt $p >> users 22 | done 23 | 24 | # If input file was created 25 | if [ -f "users" ]; then 26 | mkdir -p $BERKELEYDBDIR 27 | # Berkeley DB is access restricted to root only 28 | db_load -T -t hash -f users $BERKELEYDBDIR"/"$BERKELEYDBFILENAME 29 | rm -rf users 30 | fi 31 | -------------------------------------------------------------------------------- /test/integration/caches_test.go: -------------------------------------------------------------------------------- 1 | // +build integration 2 | 3 | package integration_test 4 | 5 | import ( 6 | "net/http" 7 | "strconv" 8 | 9 | . "github.com/onsi/ginkgo" 10 | 11 | "gopkg.in/gavv/httpexpect.v1" 12 | 13 | "github.com/intel/rmd/modules/cache" 14 | testhelpers "github.com/intel/rmd/test/test_helpers" 15 | ) 16 | 17 | var cacheSchemaTemplate string = `{ 18 | "type": "object", 19 | "properties": { 20 | "rdt": {{.bool}}, 21 | "cqm": {{.bool}}, 22 | "cdp": {{.bool}}, 23 | "cdp_enable": {{.bool}}, 24 | "cat": {{.bool}}, 25 | "cat_enable": {{.bool}}, 26 | "caches": { 27 | "type": "object", 28 | "properties": { 29 | "l3": { 30 | "type": "object", 31 | "properties": { 32 | "number": {{.pint}}, 33 | "cache_ids": {"type": "array", "items": {{.uint}}} 34 | } 35 | }, 36 | "l2": { 37 | "type": "object", 38 | "properties": { 39 | "number": {{.pint}}, 40 | "cache_ids": {"type": "array", "items": {{.uint}}} 41 | } 42 | } 43 | } 44 | } 45 | }, 46 | "required": ["rdt", "cqm", "cdp", "cdp_enable", "cat", "cat_enable", "caches"] 47 | }` 48 | 49 | // Caches is capital ? fix it ? 50 | var cacheLevelSchemaTemplate string = `{ 51 | "type": "object", 52 | "properties": { 53 | "number": {{.pint}}, 54 | "Caches": { 55 | "type": "object" 56 | } 57 | } 58 | }` 59 | 60 | var _ = Describe("Caches", func() { 61 | 62 | var ( 63 | v1url string 64 | he *httpexpect.Expect 65 | cacheSchema string 66 | cacheLevelSchema string 67 | llc uint32 68 | ) 69 | 70 | BeforeEach(func() { 71 | By("set url") 72 | v1url = testhelpers.GetHTTPV1URL() 73 | he = httpexpect.New(GinkgoT(), v1url) 74 | }) 75 | 76 | AfterEach(func() { 77 | }) 78 | 79 | Describe("Get the new system", func() { 80 | Context("when request 'cache' API", func() { 81 | BeforeEach(func() { 82 | By("Set cache schemata") 83 | cacheSchema, _ = testhelpers.FormatByKey(cacheSchemaTemplate, 84 | map[string]interface{}{ 85 | "bool": testhelpers.BoolSchema, 86 | "pint": testhelpers.PositiveInteger, 87 | "uint": testhelpers.NonNegativeInteger}) 88 | }) 89 | 90 | It("Should be return 200", func() { 91 | 92 | repos := he.GET("/cache"). 93 | WithHeader("Content-Type", "application/json"). 94 | Expect(). 95 | Status(http.StatusOK). 96 | JSON() 97 | 98 | repos.Schema(cacheSchema) 99 | }) 100 | }) 101 | 102 | Context("when request 'cache' API by level", func() { 103 | BeforeEach(func() { 104 | By("Set cache level schemata") 105 | cacheLevelSchema, _ = testhelpers.FormatByKey(cacheLevelSchemaTemplate, 106 | map[string]interface{}{ 107 | "pint": testhelpers.PositiveInteger, 108 | }) 109 | 110 | llc = cache.GetLLC() 111 | }) 112 | 113 | It("Should be return 200 for llc ", func() { 114 | 115 | repos := he.GET("/cache/llc"). 116 | WithHeader("Content-Type", "application/json"). 117 | Expect(). 118 | Status(http.StatusOK). 119 | JSON() 120 | repos.Schema(cacheLevelSchema) 121 | }) 122 | 123 | It("Should be return 200 for last level", func() { 124 | 125 | target_lev := strconv.FormatUint(uint64(llc), 10) 126 | cacheUrl := "/cache/l" + target_lev 127 | 128 | repos := he.GET(cacheUrl). 129 | WithHeader("Content-Type", "application/json"). 130 | Expect(). 131 | Status(http.StatusOK). 132 | JSON() 133 | repos.Schema(cacheLevelSchema) 134 | }) 135 | 136 | It("Should be return 400 for not last level", func() { 137 | 138 | badllc := llc - 1 139 | target_lev := strconv.FormatUint(uint64(badllc), 10) 140 | cacheUrl := "/cache/l" + target_lev 141 | 142 | he.GET(cacheUrl). 143 | WithHeader("Content-Type", "application/json"). 144 | Expect(). 145 | Status(http.StatusBadRequest) 146 | }) 147 | 148 | }) 149 | }) 150 | }) 151 | -------------------------------------------------------------------------------- /test/integration/integration.go: -------------------------------------------------------------------------------- 1 | // +build integration 2 | 3 | package integration 4 | 5 | // For integration test, we chose BDD testcase framework. 6 | 7 | // https://github.com/onsi/ginkgo 8 | // More details please ref: http://onsi.github.io/ginkgo/ 9 | 10 | // RMD is a restful API server, for RESTFUL assert we use httpexpect 11 | // https://github.com/gavv/httpexpect 12 | // Install httpexpect: $ go get gopkg.in/gavv/httpexpect.v1 13 | // Also install other dependency: 14 | // $ go get github.com/dgrijalva/jwt-go 15 | // $ go get github.com/labstack/echo 16 | // $ go get "google.golang.org/appengine" 17 | // $ go get "gopkg.in/kataras/iris.v6" 18 | 19 | // In ordre to distinguish unit test and integration test, we use -short as the 20 | // option of "go test" 21 | 22 | // There are already some unit tests in RMD source code. 23 | // We can Converte the Existing Tests to ginkgo by: 24 | // $ ginkgo convert github.com/your/package 25 | // Ref: http://onsi.github.io/ginkgo/#converting-existing-tests 26 | -------------------------------------------------------------------------------- /test/integration/integration_suite_test.go: -------------------------------------------------------------------------------- 1 | // +build integration 2 | 3 | package integration_test 4 | 5 | import ( 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | "fmt" 10 | "os" 11 | "testing" 12 | 13 | "github.com/intel/rmd/test/test_helpers" 14 | "github.com/intel/rmd/utils/config" 15 | "github.com/intel/rmd/utils/flag" 16 | "github.com/intel/rmd/utils/resctrl" 17 | ) 18 | 19 | func TestIntegration(t *testing.T) { 20 | RegisterFailHandler(Fail) 21 | RunSpecs(t, "Integration Suite") 22 | } 23 | 24 | var v1url string 25 | var PidNumber = 5 26 | var Pids []*os.Process 27 | 28 | var _ = BeforeSuite(func() { 29 | err := testhelpers.ConfigInit(os.Getenv("CONF")) 30 | Expect(err).NotTo(HaveOccurred()) 31 | flag.InitFlags() 32 | 33 | if err := config.Init(); err != nil { 34 | fmt.Println("Init config failed:", err) 35 | os.Exit(1) 36 | } 37 | if err := resctrl.Init(); err != nil { 38 | fmt.Println("Init config failed:", err) 39 | os.Exit(1) 40 | } 41 | 42 | Pids, err = testhelpers.CreateNewProcesses("sleep 100", PidNumber) 43 | Expect(err).NotTo(HaveOccurred()) 44 | 45 | v1url = testhelpers.GetHTTPV1URL() 46 | }) 47 | 48 | var _ = AfterSuite(func() { 49 | //Cleanup processes 50 | testhelpers.CleanupProcesses(Pids) 51 | }) 52 | -------------------------------------------------------------------------------- /test/integration/policy_test.go: -------------------------------------------------------------------------------- 1 | // +build integration 2 | 3 | package integration_test 4 | 5 | import ( 6 | "github.com/intel/rmd/modules/policy" 7 | testhelpers "github.com/intel/rmd/test/test_helpers" 8 | . "github.com/onsi/ginkgo" 9 | "gopkg.in/gavv/httpexpect.v1" 10 | "net/http" 11 | ) 12 | 13 | var _ = Describe("Policy", func() { 14 | 15 | var ( 16 | v1url string 17 | he *httpexpect.Expect 18 | policies policy.Policy 19 | ) 20 | 21 | BeforeEach(func() { 22 | By("set url") 23 | v1url = testhelpers.GetHTTPV1URL() 24 | he = httpexpect.New(GinkgoT(), v1url) 25 | }) 26 | 27 | AfterEach(func() { 28 | }) 29 | 30 | Describe("Get the new system", func() { 31 | Context("when request 'policy' API", func() { 32 | BeforeEach(func() { 33 | }) 34 | 35 | It("Should be return 200", func() { 36 | // policy returns an array 37 | policies, _ = policy.LoadPolicyInfo() 38 | reparr := he.GET("/policy"). 39 | WithHeader("Content-Type", "application/json"). 40 | Expect(). 41 | Status(http.StatusOK). 42 | JSON() 43 | reparr.NotNull() 44 | reparr.Equal(policies) 45 | }) 46 | }) 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /test/integrationHTTPS/cert_https_auth_test.go: -------------------------------------------------------------------------------- 1 | // +build integrationHTTPS 2 | 3 | package integrationHTTPS 4 | 5 | import ( 6 | . "github.com/onsi/ginkgo" 7 | ) 8 | 9 | var _ = Describe("CertAuth", func() { 10 | }) 11 | -------------------------------------------------------------------------------- /test/integrationHTTPS/suite_test.go: -------------------------------------------------------------------------------- 1 | // +build integrationHTTPS 2 | 3 | package integrationHTTPS 4 | 5 | import ( 6 | "crypto/tls" 7 | "github.com/intel/rmd/test/test_helpers" 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | "gopkg.in/gavv/httpexpect.v1" 11 | "net/http" 12 | "os" 13 | "testing" 14 | ) 15 | 16 | var ( 17 | he *httpexpect.Expect 18 | ) 19 | 20 | func Test(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "Integration HTTPS Suite") 23 | } 24 | 25 | var _ = BeforeSuite(func() { 26 | 27 | err := testhelpers.ConfigInit(os.Getenv("CONF")) 28 | Expect(err).NotTo(HaveOccurred()) 29 | 30 | skipVerify := false 31 | if testhelpers.GetClientAuthType() == "no" { 32 | skipVerify = true 33 | } 34 | 35 | he = httpexpect.WithConfig(httpexpect.Config{ 36 | BaseURL: testhelpers.GetHTTPSV1URL(), 37 | Client: &http.Client{ 38 | Transport: &http.Transport{ 39 | TLSClientConfig: &tls.Config{ 40 | InsecureSkipVerify: skipVerify}, 41 | }, 42 | }, 43 | Reporter: httpexpect.NewAssertReporter( 44 | httpexpect.NewAssertReporter(GinkgoT()), 45 | ), 46 | Printers: []httpexpect.Printer{ 47 | httpexpect.NewDebugPrinter(GinkgoT(), true), 48 | }, 49 | }) 50 | 51 | }) 52 | -------------------------------------------------------------------------------- /test/test_helpers/schema.go: -------------------------------------------------------------------------------- 1 | package testhelpers 2 | 3 | // BoolSchema schema 4 | var BoolSchema = `{ 5 | "type": "boolean" 6 | }` 7 | 8 | // PositiveInteger schema 9 | var PositiveInteger = `{ 10 | "type": ["integer", "string"], 11 | "pattern": "^[0-9]*$", "minimum": 1 12 | }` 13 | 14 | // NonNegativeInteger schema 15 | var NonNegativeInteger = `{ 16 | "type": ["integer", "string"], 17 | "pattern": "^[0-9]*$", "minimum": 0 18 | }` 19 | -------------------------------------------------------------------------------- /test/test_helpers/utils_test.go: -------------------------------------------------------------------------------- 1 | package testhelpers 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func TestCreateNewProcess(t *testing.T) { 10 | p, err := CreateNewProcess("sleep 10") 11 | if err != nil { 12 | t.Errorf("error %v", err) 13 | } 14 | 15 | fmt.Println(p.Pid) 16 | err = p.Kill() 17 | if err != nil { 18 | t.Errorf("error %v", err) 19 | } 20 | 21 | } 22 | 23 | func TestAssembleRequest(t *testing.T) { 24 | p, _ := CreateNewProcess("sleep 10") 25 | fmt.Println(AssembleRequest([]*os.Process{p}, []string{}, 10, 10, -1, "")) 26 | CleanupProcess(p) 27 | } 28 | 29 | func TestAssembleRequestMultipleProcess(t *testing.T) { 30 | ps, _ := CreateNewProcesses("sleep 10", 3) 31 | fmt.Println(AssembleRequest(ps, []string{}, 10, 10, -1, "")) 32 | CleanupProcesses(ps) 33 | } 34 | 35 | func TestAssembleRequestCPUs(t *testing.T) { 36 | fmt.Println(AssembleRequest([]*os.Process{}, []string{"1-2"}, 10, 10, -1, "")) 37 | } 38 | 39 | func TestFormatByKey(t *testing.T) { 40 | m := map[string]interface{}{"w": "world"} 41 | s := "Hello {{.w}}!" 42 | r, _ := FormatByKey(s, m) 43 | if r != "Hello world!" { 44 | t.Errorf("error for format string: %s", s) 45 | } 46 | 47 | m = map[string]interface{}{"age": "47", "name": "John", "key": "no use"} 48 | s = "Hi {{.name}}. Your age is {{.age}}\n" 49 | r, _ = FormatByKey(s, m) 50 | if r != "Hi John. Your age is 47\n" { 51 | t.Errorf("error for format string: %s", s) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /utils/acl/acl.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import ( 4 | "fmt" 5 | "path" 6 | "path/filepath" 7 | "regexp" 8 | "strings" 9 | "sync" 10 | 11 | "github.com/casbin/casbin" 12 | "github.com/emicklei/go-restful" 13 | log "github.com/sirupsen/logrus" 14 | 15 | "github.com/intel/rmd/utils/acl/config" 16 | ) 17 | 18 | // Enforcer does enforce 19 | type Enforcer struct { 20 | url *casbin.Enforcer 21 | ip *casbin.Enforcer 22 | protocol *casbin.Enforcer 23 | } 24 | 25 | // VersionTrim is ... 26 | var VersionTrim = regexp.MustCompile(`^/v\d+/`) 27 | var enforcer = &Enforcer{} 28 | var once sync.Once 29 | 30 | // NewEnforcer creates enforcer 31 | func NewEnforcer() (*Enforcer, error) { 32 | var returnErr error 33 | defer func() { 34 | if r := recover(); r != nil { 35 | returnErr = fmt.Errorf("init Enforcer error: %s", r) 36 | } 37 | }() 38 | 39 | once.Do(func() { 40 | aclconf := config.NewACLConfig() 41 | for _, filter := range strings.Split(aclconf.Filter, ",") { 42 | model := path.Join(aclconf.Path, filter, "model.conf") 43 | policy := path.Join(aclconf.Path, filter, "policy.csv") 44 | switch filter { 45 | case "url": 46 | e := casbin.NewEnforcer(model, policy) 47 | // NOTE, the policy file should define a role named user. 48 | e.AddRoleForUser(config.CertClientUserRole, "user") 49 | enforcer.url = e 50 | log.Infof("succssfully set %s acl", filter) 51 | case "ip": 52 | log.Infof("Do not support %s acl at present", filter) 53 | case "proto": 54 | log.Infof("Do not support %s acl at present", filter) 55 | default: 56 | log.Errorf("Unknow acl type %s", filter) 57 | } 58 | } 59 | }) 60 | return enforcer, returnErr 61 | } 62 | 63 | // Enforce does enforce based on request 64 | func (e *Enforcer) Enforce(request *restful.Request, sub string) bool { 65 | allow := false 66 | obj := VersionTrim.ReplaceAllString(path.Clean(request.Request.RequestURI), "/") 67 | act := request.Request.Method 68 | if e.url != nil { 69 | allow = e.url.Enforce(sub, obj, act) 70 | } else { 71 | allow = true 72 | } 73 | // TODO support ip and proto Enforce. 74 | return allow 75 | } 76 | 77 | // GetAdminCerts Get all Admin certification files from a given path. 78 | func GetAdminCerts() ([]string, error) { 79 | aclconf := config.NewACLConfig() 80 | if aclconf.AdminCert == "" { 81 | return []string{}, nil 82 | } 83 | 84 | return filepath.Glob(aclconf.AdminCert + "/*.pem") 85 | } 86 | 87 | // GetUserCerts Get all user certification files from a given path. 88 | func GetUserCerts() ([]string, error) { 89 | aclconf := config.NewACLConfig() 90 | if aclconf.UserCert == "" { 91 | return []string{}, nil 92 | } 93 | 94 | return filepath.Glob(aclconf.UserCert + "/*.pem") 95 | } 96 | 97 | // GetCertsPath Get all certification files include admin's and common user's from a given path. 98 | func GetCertsPath() []string { 99 | aclconf := config.NewACLConfig() 100 | return []string{aclconf.AdminCert, aclconf.UserCert} 101 | } 102 | -------------------------------------------------------------------------------- /utils/acl/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "strings" 5 | "sync" 6 | 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | // Author describes a set of authorization. 11 | type Author uint32 12 | 13 | // These are the generalized file operations that can trigger a notification. 14 | const ( 15 | Signature Author = 1 << iota 16 | OU 17 | CN 18 | ) 19 | 20 | const ( 21 | // CertClientUserRole is user role of the client 22 | CertClientUserRole = "Cert Client User" 23 | ) 24 | 25 | // ACL what? 26 | type ACL struct { 27 | Path string `toml:"path"` 28 | Filter string `toml:"filter"` 29 | AdminCert string `toml:"admincert"` 30 | UserCert string `toml:"usercert"` 31 | Authorization Author 32 | } 33 | 34 | var authmap = map[string]Author{ 35 | "signature": Signature, 36 | "role": OU, 37 | "username": CN, 38 | } 39 | 40 | var once sync.Once 41 | var acl = &ACL{"/etc/rmd/acl/", "url", "", "", Signature} 42 | 43 | // NewACLConfig create new ACL config 44 | func NewACLConfig() *ACL { 45 | once.Do(func() { 46 | viper.UnmarshalKey("acl", acl) 47 | a := strings.ToLower(viper.GetString("acl.authorization")) 48 | if v, ok := authmap[a]; ok { 49 | acl.Authorization = v 50 | } 51 | }) 52 | return acl 53 | } 54 | -------------------------------------------------------------------------------- /utils/auth/pam.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | 7 | "github.com/emicklei/go-restful" 8 | proxyclient "github.com/intel/rmd/internal/proxy/client" 9 | ) 10 | 11 | // PAMAuthenticate does PAM authenticate with PAM DB or PAM Unix 12 | func PAMAuthenticate(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { 13 | 14 | // PAM enabled only for HTTPS requests 15 | if req.Request.TLS == nil { 16 | chain.ProcessFilter(req, resp) 17 | return 18 | } 19 | 20 | // Get user credentials 21 | u, p, ok := req.Request.BasicAuth() 22 | 23 | if !ok { 24 | resp.WriteErrorString(http.StatusBadRequest, "Malformed credentials\n") 25 | return 26 | } 27 | 28 | // PAM authenticate 29 | err := proxyclient.PAMAuthenticate(u, p) 30 | 31 | if err != nil { 32 | resp.AddHeader("WWW-Authenticate", "Basic realm=RMD") 33 | var buffer bytes.Buffer 34 | buffer.WriteString(err.Error()) 35 | buffer.WriteString("\n") 36 | resp.WriteErrorString(http.StatusUnauthorized, buffer.String()) 37 | return 38 | } 39 | 40 | chain.ProcessFilter(req, resp) 41 | } 42 | -------------------------------------------------------------------------------- /utils/bitmap/bitmaputil_test.go: -------------------------------------------------------------------------------- 1 | package bitmap 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIsZeroHexString(t *testing.T) { 8 | 9 | cpus := "000000,00000000,00000000" 10 | if !IsZeroHexString(cpus) { 11 | t.Errorf("Misjudgement, '%s' is a string with all zero.\n", cpus) 12 | } 13 | 14 | cpus = "000000,00000000,00000001" 15 | if IsZeroHexString(cpus) { 16 | t.Errorf("Misjudgement, '%s' is not a string with all zero.\n", cpus) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /utils/bootcheck/bootcheck.go: -------------------------------------------------------------------------------- 1 | package bootcheck 2 | 3 | import ( 4 | "fmt" 5 | 6 | log "github.com/sirupsen/logrus" 7 | 8 | "github.com/intel/rmd/internal/db" 9 | proxyclient "github.com/intel/rmd/internal/proxy/client" 10 | "github.com/intel/rmd/modules/cache" 11 | "github.com/intel/rmd/utils/acl" 12 | "github.com/intel/rmd/utils/cpu" 13 | ) 14 | 15 | // SanityCheck before string rmd process 16 | func SanityCheck() { 17 | pf := cpu.GetMicroArch(cpu.GetSignature()) 18 | if pf == "" { 19 | msg := "Unknow platform, please update the cpu_map.toml conf file." 20 | log.Fatal(msg) 21 | } 22 | if _, err := acl.NewEnforcer(); err != nil { 23 | msg := "Error to generate an Enforcer! Reason: " + err.Error() 24 | log.Fatal(msg) 25 | } 26 | cpunum := cpu.HostCPUNum() 27 | if cpunum == 0 { 28 | msg := "Unable to get Total CPU numbers on Host." 29 | log.Fatal(msg) 30 | } 31 | 32 | if !proxyclient.IsIntelRdtMounted() { 33 | if err := proxyclient.EnableCat(); err != nil { 34 | msg := "can not enable cat" 35 | log.Fatal(msg) 36 | } 37 | } 38 | if err := DBCheck(); err != nil { 39 | msg := "Check db error. Reason: " + err.Error() 40 | log.Fatal(msg) 41 | } 42 | if err := cache.SetOSGroup(); err != nil { 43 | msg := "Error, create OS groups failed! Reason: " + err.Error() 44 | log.Fatal(msg) 45 | } 46 | if err := cache.SetInfraGroup(); err != nil { 47 | msg := "Error, create infra groups failed! Reason: " + err.Error() 48 | log.Fatal(msg) 49 | } 50 | v, err := cache.GetCachePoolLayout() 51 | log.Debugf("Cache Pool layout %v", v) 52 | if err != nil { 53 | msg := "Error while get cache pool layout Reason: " + err.Error() 54 | log.Fatal(msg) 55 | } 56 | } 57 | 58 | // DBCheck Do some cleanup in DB 59 | func DBCheck() error { 60 | d, err := db.NewDB() 61 | if err != nil { 62 | return err 63 | } 64 | 65 | err = d.Initialize("", "") 66 | if err != nil { 67 | return err 68 | } 69 | 70 | resaall := proxyclient.GetResAssociation(nil) 71 | 72 | wl, err := d.GetAllWorkload() 73 | if err != nil { 74 | return err 75 | } 76 | 77 | for _, w := range wl { 78 | switch w.CosName { 79 | case "": 80 | d.DeleteWorkload(&w) 81 | case "os": 82 | case "OS": 83 | case ".": 84 | case "infra": 85 | // FIXME Now we can allow to create multi-infra, need clean? 86 | case "default": 87 | default: 88 | if v, ok := resaall[w.CosName]; !ok { 89 | d.DeleteWorkload(&w) 90 | fmt.Println(v) 91 | } 92 | // FIXME, delete the group with null tasks and zero cpus. 93 | } 94 | } 95 | return nil 96 | 97 | } 98 | -------------------------------------------------------------------------------- /utils/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "sync" 7 | 8 | "github.com/spf13/pflag" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | // ClientAuth is a string to tls clientAuthType map 13 | var ClientAuth = map[string]tls.ClientAuthType{ 14 | "no": tls.NoClientCert, 15 | "require": tls.RequestClientCert, 16 | "require_any": tls.RequireAnyClientCert, 17 | "challenge_given": tls.VerifyClientCertIfGiven, 18 | "challenge": tls.RequireAndVerifyClientCert, 19 | } 20 | 21 | const ( 22 | // CAFile is the certificate authority file 23 | CAFile = "ca.pem" 24 | // CertFile is the certificate file 25 | CertFile = "rmd-cert.pem" 26 | // KeyFile is the rmd private key file 27 | KeyFile = "rmd-key.pem" 28 | // ClientCAFile certificate authority file of client side 29 | ClientCAFile = "ca.pem" 30 | ) 31 | 32 | // Default is the configuration in default section of config file 33 | // TODO consider create a new struct for TLSConfig 34 | type Default struct { 35 | Address string `toml:"address"` 36 | TLSPort uint `toml:"tlsport"` 37 | CertPath string `toml:"certpath"` 38 | ClientCAPath string `toml:"clientcapath"` 39 | ClientAuth string `toml:"clientauth"` 40 | UnixSock string `toml:"unixsock"` 41 | PolicyPath string `toml:"policypath"` 42 | OpenStackEnable bool `toml:"openstackenable"` 43 | SysResctrl string `toml:"sysresctrl"` 44 | Plugins string `toml:"plugins"` 45 | DbValidatorInterval uint `toml:"dbValidatorInterval"` 46 | } 47 | 48 | // Database represents data base configuration 49 | type Database struct { 50 | Backend string `toml:"backend"` 51 | Transport string `toml:"transport"` 52 | DBName string `toml:"dbname"` 53 | } 54 | 55 | // Debug configurations 56 | type Debug struct { 57 | Enabled bool `toml:"enabled"` 58 | Debugport uint `toml:"debugport"` 59 | } 60 | 61 | // Config represent the configuration struct 62 | type Config struct { 63 | Def Default `mapstructure:"default"` 64 | Db Database `mapstructure:"database"` 65 | Dbg Debug `mapstructure:"debug"` 66 | } 67 | 68 | var configOnce sync.Once 69 | var def = Default{ 70 | "localhost", 71 | 8443, 72 | "/etc/rmd/cert/server", 73 | "/etc/rmd/cert/client", 74 | "challenge", 75 | "", 76 | "/etc/rmd/policy.toml", 77 | false, 78 | "/sys/fs/resctrl", 79 | "", // by default do not load any external plugin (if not configured) 80 | 30, 81 | } 82 | 83 | var defaultConfigPath = []string{ 84 | "/etc/rmd/", 85 | } 86 | 87 | var db = Database{} 88 | var dbg = Debug{} 89 | var config = &Config{def, db, dbg} 90 | 91 | // NewConfig loads configurations from config file and pflag 92 | func NewConfig() Config { 93 | 94 | configOnce.Do(func() { 95 | 96 | // Take the value from pflag which was defined in flag.go 97 | // The default value of the struct as not taken if we 98 | // bind pflag to viper 99 | viper.BindPFlag("default.address", pflag.Lookup("address")) 100 | viper.BindPFlag("default.tlsport", pflag.Lookup("tlsport")) 101 | viper.BindPFlag("default.unixsock", pflag.Lookup("unixsock")) 102 | viper.BindPFlag("default.clientauth", pflag.Lookup("clientauth")) 103 | viper.BindPFlag("debug.enabled", pflag.Lookup("debug")) 104 | viper.BindPFlag("debug.debugport", pflag.Lookup("debugport")) 105 | 106 | err := viper.Unmarshal(config) 107 | if err != nil { 108 | panic(err) 109 | } 110 | }) 111 | 112 | return *config 113 | } 114 | 115 | // Init does config initial 116 | func Init() error { 117 | viper.SetConfigName("rmd") // no need to include file extension 118 | // TODO consider to introduce Cobra. let Viper work with Cobra. 119 | confDir := pflag.Lookup("conf-dir").Value.String() 120 | if confDir != "" { 121 | viper.AddConfigPath(confDir) 122 | } 123 | for _, p := range defaultConfigPath { 124 | viper.AddConfigPath(p) 125 | } 126 | err := viper.ReadInConfig() 127 | if err != nil { 128 | // NOTE (ShaoHe Feng): only can use fmt.Println, can not use log. 129 | // For log is not init at this point. 130 | fmt.Printf("Error while reading config file: %v\n", err) 131 | } 132 | return nil 133 | } 134 | -------------------------------------------------------------------------------- /utils/cpu/cpu.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "bufio" 5 | "io/ioutil" 6 | "os" 7 | "path/filepath" 8 | "strconv" 9 | "strings" 10 | "sync" 11 | 12 | log "github.com/sirupsen/logrus" 13 | ) 14 | 15 | const ( 16 | // SysCPU is the path to cpu devices in linux 17 | SysCPU = "/sys/devices/system/cpu" 18 | // CPUInfoPath is the path to cpu info in /proc 19 | CPUInfoPath = "/proc/cpuinfo" 20 | ) 21 | 22 | var cpunOnce sync.Once 23 | var isolCPUsOnce sync.Once 24 | 25 | var cpuNumber int 26 | var isolatedCPUs string 27 | 28 | // HostCPUNum returns the total cpu number of host 29 | // REF: https://www.kernel.org/doc/Documentation/cputopology.txt 30 | // another way is call sysconf via cgo, like libpqos 31 | func HostCPUNum() int { 32 | cpunOnce.Do(func() { 33 | path := filepath.Join(SysCPU, "possible") 34 | data, _ := ioutil.ReadFile(path) 35 | strs := strings.TrimSpace(string(data)) 36 | num, err := strconv.Atoi(strings.SplitN(strs, "-", 2)[1]) 37 | if err != nil { 38 | log.Fatalf("Failed to get cup numbers on host: %v", err) 39 | return 40 | } 41 | num++ 42 | cpuNumber = num 43 | }) 44 | return cpuNumber 45 | } 46 | 47 | // GetSignature returns Signature of the processor 48 | // ignore stepping and processor type. 49 | // NOTE, Guess all cpus in one hose are same microarch 50 | func GetSignature() uint32 { 51 | // family, model string 52 | var family, model int 53 | 54 | f, err := os.Open(CPUInfoPath) 55 | if err != nil { 56 | return 0 57 | } 58 | defer f.Close() 59 | 60 | br := bufio.NewReader(f) 61 | find := 0 62 | 63 | for err == nil { 64 | // NOTE: ReadString() function does not cause any DOS Resource Exhaustion here 65 | // since the file being read is system file (/proc/cpuinfo). 66 | s, err := br.ReadString('\n') 67 | if err != nil { 68 | return 0 69 | } 70 | if strings.HasPrefix(s, "cpu family") { 71 | sl := strings.Split(s, ":") 72 | family, _ = strconv.Atoi(strings.TrimSpace(sl[1])) 73 | find++ 74 | } else if strings.HasPrefix(s, "model") { 75 | sl := strings.Split(s, ":") 76 | if strings.TrimSpace(sl[0]) == "model" { 77 | model, _ = strconv.Atoi(strings.TrimSpace(sl[1])) 78 | find++ 79 | } 80 | } 81 | if find >= 2 { 82 | sig := (family>>4)<<20 + (family&0xf)<<8 83 | sig |= (model>>4)<<16 + (model&0xf)<<4 84 | return uint32(sig) 85 | } 86 | } 87 | return 0 88 | } 89 | 90 | // IsolatedCPUs returns isolated CPUs. 91 | // The result will be as follow: 92 | // 2-21,24-43,46-65,68-87 93 | // This result can generate a Bitmap 94 | func IsolatedCPUs() string { 95 | isolCPUsOnce.Do(func() { 96 | path := filepath.Join(SysCPU, "isolated") 97 | data, _ := ioutil.ReadFile(path) 98 | strs := strings.TrimSpace(string(data)) 99 | isolatedCPUs = strs 100 | }) 101 | return isolatedCPUs 102 | } 103 | 104 | // LocateOnSocket return the cpus on which socket. 105 | func LocateOnSocket(cpuid string) (id string, err error) { 106 | path := filepath.Join(SysCPU, "cpu"+cpuid, "topology/physical_package_id") 107 | data, err := ioutil.ReadFile(path) 108 | if err != nil { 109 | return "", err 110 | } 111 | id = strings.TrimSpace(string(data)) 112 | return id, err 113 | } 114 | 115 | // LocateOnNode return the cpus on which node. 116 | func LocateOnNode(cpuid string) string { 117 | pattern := filepath.Join(SysCPU, "cpu"+cpuid, "node*") 118 | files, _ := filepath.Glob(pattern) 119 | if len(files) > 0 { 120 | _, file := filepath.Split(files[0]) 121 | nodeid := strings.Split(file, "node") 122 | return nodeid[1] 123 | } 124 | return "" 125 | } 126 | -------------------------------------------------------------------------------- /utils/cpu/cpu_test.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGetSignature(t *testing.T) { 9 | sig := GetSignature() 10 | fmt.Printf("CPU signature is 0x%x.\n", sig) 11 | if sig == 0 { 12 | t.Errorf("CPU signature should be >0.\n") 13 | } 14 | } 15 | 16 | func TestGetMicroArch(t *testing.T) { 17 | m := GetMicroArch(0x50650) 18 | fmt.Println("CPU MicroArch is", m) 19 | if m != "Skylake" { 20 | t.Errorf("CPU MicroArch should be %s.\n", "Skylake") 21 | } 22 | 23 | m = GetMicroArch(0x50659) 24 | fmt.Println("CPU MicroArch is", m) 25 | if m != "Skylake" { 26 | t.Errorf("CPU MicroArch should be %s.\n", "Skylake") 27 | } 28 | } 29 | 30 | func TestLocateOnSocket(t *testing.T) { 31 | id, err := LocateOnSocket("0") 32 | if id != "0" { 33 | t.Errorf("CPU MicroArch should be %s.\n", "Skylake") 34 | } 35 | cpuid := "100000" 36 | id, err = LocateOnSocket(cpuid) 37 | if err == nil || id != "" { 38 | t.Errorf("Given a very large cpuid: %s, Should not get the socket id.", cpuid) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /utils/cpu/microarch.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "github.com/spf13/viper" 5 | 6 | "fmt" 7 | "sync" 8 | ) 9 | 10 | // ignore stepping 11 | var m = map[uint32]string{ 12 | 0x406e0: "Skylake", 13 | 0x506e0: "Skylake", 14 | 0x50650: "Skylake", 15 | 0x806e0: "Kaby Lake", 16 | 0x906e0: "Kaby Lake", 17 | 0x306d0: "Broadwell", 18 | 0x406f0: "Broadwell", 19 | } 20 | 21 | type microArch struct { 22 | Family int `toml:"family"` 23 | Model int `toml:"model"` 24 | } 25 | 26 | var cpumapOnce sync.Once 27 | var defaultConfigPath = []string{ 28 | "/etc/rmd/", 29 | } 30 | 31 | // NewCPUMap init internal cpu map 32 | // Concurrency safe. 33 | func NewCPUMap() map[uint32]string { 34 | cpumapOnce.Do(func() { 35 | var rtViper = viper.New() 36 | var maps = map[string][]microArch{} 37 | 38 | rtViper.SetConfigType("toml") 39 | rtViper.SetConfigName("cpu_map") 40 | for _, p := range defaultConfigPath { 41 | rtViper.AddConfigPath(p) 42 | } 43 | err := rtViper.ReadInConfig() 44 | 45 | if err != nil { 46 | // TODO using log 47 | fmt.Printf("No cpu map found from %v, fall back to using default cpu map\n", defaultConfigPath) 48 | } 49 | 50 | rtViper.Unmarshal(&maps) 51 | for k, mv := range maps { 52 | for _, v := range mv { 53 | sig := (v.Family>>4)<<20 + (v.Family&0xf)<<8 + (v.Model>>4)<<16 + (v.Model&0xf)<<4 54 | m[uint32(sig)] = k 55 | } 56 | } 57 | 58 | }) 59 | return m 60 | } 61 | 62 | // GetMicroArch returns micro arch 63 | func GetMicroArch(sig uint32) string { 64 | s := sig & 0xFFFF0FF0 65 | NewCPUMap() 66 | if v, ok := m[s]; ok { 67 | return v 68 | } 69 | return "" 70 | } 71 | -------------------------------------------------------------------------------- /utils/flag/flags.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | package flag 15 | 16 | import ( 17 | goflag "flag" 18 | "strings" 19 | 20 | "github.com/golang/glog" 21 | "github.com/spf13/pflag" 22 | ) 23 | 24 | // WordSepNormalizeFunc changes all flags that contain "_" separators 25 | func WordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { 26 | if strings.Contains(name, "_") { 27 | return pflag.NormalizedName(strings.Replace(name, "_", "-", -1)) 28 | } 29 | return pflag.NormalizedName(name) 30 | } 31 | 32 | // WarnWordSepNormalizeFunc changes and warns for flags that contain "_" separators 33 | func WarnWordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { 34 | if strings.Contains(name, "_") { 35 | nname := strings.Replace(name, "_", "-", -1) 36 | glog.Warningf("%s is DEPRECATED and will be removed in a future version. Use %s instead.", name, nname) 37 | 38 | return pflag.NormalizedName(nname) 39 | } 40 | return pflag.NormalizedName(name) 41 | } 42 | 43 | // InitFlags normalizes and parses the command line flags 44 | func InitFlags() { 45 | pflag.CommandLine.SetNormalizeFunc(WordSepNormalizeFunc) 46 | pflag.CommandLine.AddGoFlagSet(goflag.CommandLine) 47 | 48 | pflag.String("address", "localhost", "Listen address") 49 | pflag.Int("tlsport", 8443, "TLS listen port") 50 | pflag.BoolP("debug", "d", false, "Enable debug") 51 | pflag.BoolP("version", "", false, "Print RMD version") 52 | pflag.String("unixsock", "", "Unix sock file path") 53 | pflag.Int("debugport", 8081, "Debug listen port") 54 | pflag.String("conf-dir", "", "Directory of config file") 55 | pflag.String("clientauth", "challenge", "The policy the server will follow for TLS Client Authentication") 56 | pflag.Bool("force-config", false, "Force settings from config file if different than current platform settings") 57 | 58 | pflag.Parse() 59 | } 60 | -------------------------------------------------------------------------------- /utils/flock/flock.go: -------------------------------------------------------------------------------- 1 | package flock 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "syscall" 7 | "time" 8 | ) 9 | 10 | // Flock acquires an advisory lock on a file descriptor. 11 | func Flock(file *os.File, timeout time.Duration, exclusive ...bool) error { 12 | 13 | lockStates := map[bool]int{true: syscall.LOCK_EX, false: syscall.LOCK_SH} 14 | flag := syscall.LOCK_SH 15 | if len(exclusive) > 0 { 16 | flag = lockStates[exclusive[0]] 17 | } 18 | 19 | s := time.Now() 20 | t := s 21 | // timeout < 0 means loop forever. timeout = 0 means just once. 22 | if timeout > 0 { 23 | t = s.Add(time.Duration(timeout)) 24 | } 25 | 26 | // A Duration represents the elapsed time between two instants as an int64 nanosecond count. 27 | // The representation limits the largest representable duration to approximately 290 years. 28 | // So here we use time Before/After 29 | for time.Duration(timeout) <= 0 || s.Before(t) { 30 | // Otherwise attempt to obtain an exclusive lock. 31 | err := syscall.Flock(int(file.Fd()), flag|syscall.LOCK_NB) 32 | if timeout == 0 { 33 | return err 34 | } 35 | if err == syscall.EWOULDBLOCK { 36 | // Wait for a bit and try again. 37 | time.Sleep(time.Millisecond * 50) 38 | s = time.Now() 39 | } else { 40 | return err 41 | } 42 | } 43 | 44 | // FIXME uniform error. 45 | return fmt.Errorf("Timeout to get flock") 46 | } 47 | 48 | // Funlock releases an advisory lock on a file descriptor. 49 | func Funlock(file *os.File) error { 50 | return syscall.Flock(int(file.Fd()), syscall.LOCK_UN) 51 | } 52 | -------------------------------------------------------------------------------- /utils/flock/flock_suite_test.go: -------------------------------------------------------------------------------- 1 | package flock_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestFlock(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Flock Suite") 13 | } 14 | -------------------------------------------------------------------------------- /utils/flock/flock_test.go: -------------------------------------------------------------------------------- 1 | package flock_test 2 | 3 | import ( 4 | "os" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | . "github.com/intel/rmd/utils/flock" 10 | ) 11 | 12 | var _ = Describe("Flock", func() { 13 | 14 | var ( 15 | fname string 16 | f *os.File 17 | err error 18 | ) 19 | 20 | BeforeEach(func() { 21 | fname = "./test.txt" 22 | }) 23 | 24 | AfterEach(func() { 25 | Funlock(f) 26 | f.Close() 27 | os.Remove(fname) 28 | }) 29 | 30 | // Separating Creation and Configuration 31 | JustBeforeEach(func() { 32 | flag := os.O_RDWR | os.O_CREATE 33 | f, err = os.OpenFile(fname, flag, 0600) 34 | if err != nil { 35 | Fail("Failed to open file") 36 | } 37 | err = Flock(f, 1000*1000*1000, true) 38 | }) 39 | 40 | Describe("Showing how to use Flock", func() { 41 | It("should not error", func() { 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | }) 45 | 46 | }) 47 | -------------------------------------------------------------------------------- /utils/log/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "strconv" 5 | "sync" 6 | 7 | // "github.com/heirko/go-contrib/logrusHelper" 8 | "github.com/spf13/pflag" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | // Log is the log config struct 13 | type Log struct { 14 | Path string `toml:"path"` 15 | Env string `toml:"env"` 16 | Level string `toml:"level"` 17 | Stdout bool `toml:"stdout"` 18 | } 19 | 20 | var configOnce sync.Once 21 | var log = &Log{"/var/log/rmd/rmd.log", "dev", "debug", false} 22 | 23 | // NewConfig loads log config 24 | func NewConfig() Log { 25 | configOnce.Do(func() { 26 | // FIXME , we are planing to use logrusHelper. Seems we still 27 | // need missing some initialization for logrus. But it repors error as 28 | // follow: 29 | // # github.com/heirko/go-contrib/logrusHelper 30 | // undefined: logrus_mate.LoggerConfig 31 | // var c = logrusHelper.UnmarshalConfiguration(viper) // Unmarshal configuration from Viper 32 | // logrusHelper.SetConfig(logrus.StandardLogger(), c) // for e.g. apply it to logrus default instance 33 | 34 | viper.UnmarshalKey("log", log) 35 | 36 | logDir := pflag.Lookup("log-dir").Value.String() 37 | if logDir != "" { 38 | log.Path = logDir 39 | } 40 | 41 | // FIXME , we should get the value of logtostderr by reflect 42 | // or flag directly, instead of strconv.ParseBool 43 | tostd, _ := strconv.ParseBool(pflag.Lookup("logtostderr").Value.String()) 44 | if tostd == true { 45 | log.Stdout = true 46 | } 47 | }) 48 | 49 | return *log 50 | } 51 | -------------------------------------------------------------------------------- /utils/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/sirupsen/logrus" 7 | 8 | "github.com/intel/rmd/utils/log/config" 9 | ) 10 | 11 | // Init does log config init 12 | // TODO : Need to support Model name and file line fields. 13 | func Init() error { 14 | config := config.NewConfig() 15 | l, _ := logrus.ParseLevel(config.Level) 16 | // FIXME (shaohe), we do not support both stdout and file at the same time. 17 | if config.Stdout { 18 | logrus.SetOutput(os.Stdout) 19 | } else { 20 | f, err := os.OpenFile( 21 | config.Path, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0660) 22 | if err != nil { 23 | return err 24 | } 25 | logrus.SetOutput(f) 26 | } 27 | 28 | logrus.SetLevel(l) 29 | if config.Env == "production" { 30 | logrus.SetFormatter(&logrus.JSONFormatter{}) 31 | } 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /utils/pidfile/pidfile.go: -------------------------------------------------------------------------------- 1 | package pidfile 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "path/filepath" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // PIDFile Pid file of rmd process 13 | const PIDFile = "/var/run/rmd.pid" 14 | 15 | // CreatePID Create pid file and write my pid into that file 16 | func CreatePID() error { 17 | // FIXME one edge case does not handle, that is the process exist, 18 | // but pidfile is removed. Beside call shell command "lsof", I have no idea. 19 | // TODO should we consider owner of pid file? 20 | 21 | // check pid file exist. 22 | dat, err := ioutil.ReadFile(PIDFile) 23 | if err == nil { 24 | pidstr := strings.TrimSpace(string(dat)) 25 | files, _ := filepath.Glob("/proc/" + pidstr) 26 | if len(files) > 0 { 27 | return fmt.Errorf("RMD %s is already running, exit", pidstr) 28 | } 29 | // From golang doc, os.FindProcess always return successful. 30 | // err == nil and process.Pid > 0 31 | } 32 | 33 | flag := os.O_RDWR | os.O_CREATE | os.O_TRUNC 34 | f, err := os.OpenFile(PIDFile, flag, 0644) 35 | if err != nil { 36 | return err 37 | } 38 | defer f.Close() 39 | 40 | pid := strconv.Itoa(os.Getpid()) 41 | _, err = f.Write([]byte(pid)) 42 | if err != nil { 43 | return err 44 | } 45 | return nil 46 | // use Flock to check the whether the process exists? Which is better. 47 | // return Flock(f, 0, true) 48 | } 49 | 50 | // ClosePID Force remove pid file 51 | func ClosePID() { 52 | dat, err := ioutil.ReadFile(PIDFile) 53 | if err == nil { 54 | p := strings.TrimSpace(string(dat)) 55 | pid := strconv.Itoa(os.Getpid()) 56 | if p == pid { 57 | os.Remove(PIDFile) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /utils/proc/proc_test.go: -------------------------------------------------------------------------------- 1 | package proc 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | util "github.com/intel/rmd/utils/bitmap" 9 | // Better to make that package a util package 10 | testhelpers "github.com/intel/rmd/test/test_helpers" 11 | ) 12 | 13 | func TestListProcesses(t *testing.T) { 14 | ps := ListProcesses() 15 | if len(ps) == 0 { 16 | t.Errorf("Faild to list all process\n") 17 | } 18 | 19 | } 20 | 21 | func TestGetCPUAffinity(t *testing.T) { 22 | 23 | ospid, _ := testhelpers.CreateNewProcess("sleep 100") 24 | 25 | pid := strconv.Itoa(ospid.Pid) 26 | 27 | oldaf, err := GetCPUAffinity(pid) 28 | if err != nil { 29 | t.Errorf("Failed to get CPU affinity for process id %s", pid) 30 | } 31 | 32 | // should verify the default cpu affinity 33 | fmt.Println(oldaf.ToHumanString()) 34 | 35 | af, _ := util.NewBitmap("3") 36 | 37 | err = SetCPUAffinity(pid, af) 38 | if err != nil { 39 | t.Errorf("Failed to set CPU affinity for process id %s", pid) 40 | } 41 | 42 | afset, err := GetCPUAffinity(pid) 43 | if err != nil { 44 | t.Errorf("Failed to get CPU affinity for process id %s", pid) 45 | } 46 | 47 | if af.ToHumanString() != afset.ToHumanString() { 48 | t.Errorf("Error to set CPU affinity for process id %s", pid) 49 | } 50 | 51 | testhelpers.CleanupProcess(ospid) 52 | } 53 | -------------------------------------------------------------------------------- /utils/resctrl/resctrl_test.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package resctrl 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func TestGetResAssociation(t *testing.T) { 11 | ress := GetResAssociation(nil) 12 | for name, res := range ress { 13 | if name == "CG1" { 14 | fmt.Println(name) 15 | fmt.Println(res) 16 | fmt.Println(res.CacheSchemata["L3CODE"]) 17 | } 18 | } 19 | } 20 | 21 | func TestGetRdtCosInfo(t *testing.T) { 22 | 23 | infos := GetRdtCosInfo() 24 | for name, info := range infos { 25 | fmt.Println(name) 26 | fmt.Println(info) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /utils/task/task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "container/list" 5 | ) 6 | 7 | // Task is an interface which could be added into a TaskList 8 | type Task interface { 9 | // Name of the task 10 | Name() string 11 | // Run method 12 | Run() error 13 | // Rollback if failed to run this task 14 | Rollback() error 15 | } 16 | 17 | // List is a list which could be called as transaction 18 | type List struct { 19 | taskList *list.List 20 | } 21 | 22 | // NewTaskList initials a list of task 23 | func NewTaskList(tasks []Task) *List { 24 | tl := list.New() 25 | for _, t := range tasks { 26 | tl.PushBack(t) 27 | } 28 | return &List{taskList: tl} 29 | } 30 | 31 | // Start to run this tasks list, will return the last run error 32 | func (l *List) Start() error { 33 | var fe *list.Element 34 | var runErr error 35 | 36 | for te := l.taskList.Front(); te != nil; te = te.Next() { 37 | t := te.Value.(Task) 38 | if err := t.Run(); err != nil { 39 | fe = te 40 | runErr = err 41 | break 42 | } 43 | } 44 | 45 | if runErr != nil { 46 | // Rollback 47 | for te := fe; te != nil; te = te.Prev() { 48 | t := te.Value.(Task) 49 | if err := t.Rollback(); err != nil { 50 | // Save rollback error and break? 51 | } 52 | } 53 | return runErr 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /utils/util_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestSubtractStringSlice(t *testing.T) { 10 | slice := []string{"a", "b", "c"} 11 | s := []string{"a", "c"} 12 | 13 | newslice := SubtractStringSlice(slice, s) 14 | 15 | if len(newslice) != 1 { 16 | t.Errorf("New slice length should be 1") 17 | } 18 | if newslice[0] != "b" { 19 | t.Errorf("New slice should be [\"2\"]") 20 | } 21 | } 22 | 23 | func TestUnifyMapParamsTypes(t *testing.T) { 24 | type args struct { 25 | pluginParams map[string]interface{} 26 | } 27 | ratioF64 := 1.5 28 | var myPstateFloat64 = map[string]interface{}{ 29 | "ratio": ratioF64, 30 | } 31 | 32 | var ratioI64 int64 = 1 33 | var myPstateInt64 = map[string]interface{}{ 34 | "ratio": ratioI64, 35 | } 36 | 37 | var ratioF32 float32 = 1.5 38 | var myPstateFloat32 = map[string]interface{}{ 39 | "ratio": ratioF32, 40 | } 41 | 42 | var ratioI32 int32 = 1 43 | var myPstateInt32 = map[string]interface{}{ 44 | "ratio": ratioI32, 45 | } 46 | 47 | var ratioJSONNumberF64 json.Number = "1.5" 48 | var myPstateJSONNumberF64 = map[string]interface{}{ 49 | "ratio": ratioJSONNumberF64, 50 | } 51 | 52 | var ratioJSONNumberI64 json.Number = "1" 53 | var myPstateJSONNumberI64 = map[string]interface{}{ 54 | "ratio": ratioJSONNumberI64, 55 | } 56 | 57 | paramInt64Table := []int64{8} 58 | var myParamInt64Table = map[string]interface{}{ 59 | "param": paramInt64Table, 60 | } 61 | 62 | paramInt32Table := []int32{8} 63 | var myParamInt32Table = map[string]interface{}{ 64 | "param": paramInt32Table, 65 | } 66 | 67 | paramFloat64Table := []float64{8} 68 | var myParamFloat64Table = map[string]interface{}{ 69 | "param": paramFloat64Table, 70 | } 71 | 72 | paramFloat32Table := []float32{8} 73 | var myParamFloat32Table = map[string]interface{}{ 74 | "param": paramFloat32Table, 75 | } 76 | 77 | paramString := "myText" 78 | var myParamString = map[string]interface{}{ 79 | "param": paramString, 80 | } 81 | 82 | paramBool := true 83 | var myParamBool = map[string]interface{}{ 84 | "param": paramBool, 85 | } 86 | 87 | var myParamNil = map[string]interface{}{ 88 | "param": nil, 89 | } 90 | 91 | tests := []struct { 92 | name string 93 | args args 94 | want map[string]interface{} 95 | wantErr bool 96 | }{ 97 | {"float64", args{pluginParams: myPstateFloat64}, myPstateFloat64, false}, 98 | {"float32", args{pluginParams: myPstateFloat32}, myPstateFloat64, false}, 99 | {"int32", args{pluginParams: myPstateInt32}, myPstateInt64, false}, 100 | {"json.Number to float64", args{pluginParams: myPstateJSONNumberF64}, myPstateFloat64, false}, 101 | {"json.Number to int64", args{pluginParams: myPstateJSONNumberI64}, myPstateInt64, false}, 102 | {"[]int64", args{pluginParams: myParamInt64Table}, myParamInt64Table, false}, 103 | {"[]int32", args{pluginParams: myParamInt32Table}, myParamInt64Table, false}, 104 | {"[]float64", args{pluginParams: myParamFloat64Table}, myParamFloat64Table, false}, 105 | {"[]float32", args{pluginParams: myParamFloat32Table}, myParamFloat64Table, false}, 106 | {"string", args{pluginParams: myParamString}, myParamString, false}, 107 | {"bool", args{pluginParams: myParamBool}, myParamBool, false}, 108 | {"nil", args{pluginParams: myParamNil}, myParamNil, false}, 109 | } 110 | for _, tt := range tests { 111 | t.Run(tt.name, func(t *testing.T) { 112 | got, err := UnifyMapParamsTypes(tt.args.pluginParams) 113 | if (err != nil) != tt.wantErr { 114 | t.Errorf("UnifyMapParamsTypes() error = %v, wantErr %v", err, tt.wantErr) 115 | return 116 | } 117 | if !reflect.DeepEqual(got, tt.want) { 118 | t.Errorf("UnifyMapParamsTypes() = %v, want %v", got, tt.want) 119 | } 120 | }) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | // Build information 4 | var ( 5 | Version string 6 | Revision string 7 | Branch string 8 | BuildDate string 9 | GoVersion string 10 | ) 11 | 12 | // Info provides the iterable version information. 13 | var Info = map[string]string{ 14 | "version": Version, 15 | "revision": Revision, 16 | "branch": Branch, 17 | "buildDate": BuildDate, 18 | "goVersion": GoVersion, 19 | } 20 | --------------------------------------------------------------------------------