mcis

package
v0.2.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 3, 2020 License: Apache-2.0 Imports: 23 Imported by: 1

Documentation

Index

Constants

View Source
const ActionComplete string = "None"
View Source
const ActionCreate string = "Create"
View Source
const ActionReboot string = "Reboot"
View Source
const ActionResume string = "Resume"
View Source
const ActionSuspend string = "Suspend"
View Source
const ActionTerminate string = "Terminate"
View Source
const AutoActionScaleIn string = "ScaleIn"
View Source
const AutoActionScaleOut string = "ScaleOut"

Action for mcis automation

View Source
const AutoStatusChecking string = "Checking"
View Source
const AutoStatusDetected string = "Detected"
View Source
const AutoStatusError string = "Failed"
View Source
const AutoStatusOperating string = "Operating"
View Source
const AutoStatusReady string = "Ready"

Status for mcis automation

View Source
const AutoStatusStabilizing string = "Stabilizing"
View Source
const AutoStatusSuspended string = "Suspended"
View Source
const AutoStatusTimeout string = "Timeout"
View Source
const LabelAutoGen string = "AutoGen"
View Source
const MonMetricAll string = "all"
View Source
const MonMetricCpu string = "cpu"
View Source
const MonMetricCpufreq string = "cpufreq"
View Source
const MonMetricDisk string = "disk"
View Source
const MonMetricDiskio string = "diskio"
View Source
const MonMetricMem string = "mem"
View Source
const MonMetricNet string = "net"
View Source
const MonMetricSwap string = "swap"
View Source
const SshDefaultUserName01 string = "cb-user"
View Source
const SshDefaultUserName02 string = "ubuntu"
View Source
const SshDefaultUserName03 string = "others"
View Source
const SshDefaultUserName04 string = "ec2-user"
View Source
const StatusComplete string = "None"
View Source
const StatusCreating string = "Creating"
View Source
const StatusFailed string = "Failed"
View Source
const StatusRebooting string = "Rebooting"
View Source
const StatusResuming string = "Resuming"
View Source
const StatusRunning string = "Running"
View Source
const StatusSuspended string = "Suspended"
View Source
const StatusSuspending string = "Suspending"
View Source
const StatusTerminated string = "Terminated"
View Source
const StatusTerminating string = "Terminating"

Variables

This section is empty.

Functions

func AddVmToMcis

func AddVmToMcis(wg *sync.WaitGroup, nsId string, mcisId string, vmInfoData *TbVmInfo) error

func CallGetMonitoringAsync

func CallGetMonitoringAsync(wg *sync.WaitGroup, nsID string, mcisID string, vmID string, vmIP string, method string, metric string, cmd string, returnResult *[]MonResultSimple)

func CallMilkyway

func CallMilkyway(wg *sync.WaitGroup, vmList []string, nsId string, mcisId string, vmId string, vmIp string, action string, option string, results *BenchmarkInfoArray)

func CallMonitoringAsync

func CallMonitoringAsync(wg *sync.WaitGroup, nsID string, mcisID string, vmID string, vmIP string, userName string, privateKey string, method string, cmd string, returnResult *[]SshCmdResult)

func CheckAllowedTransition

func CheckAllowedTransition(nsId string, mcisId string, action string) error

func CheckDragonflyEndpoint

func CheckDragonflyEndpoint() error

Module for checking CB-Dragonfly endpoint (call get config)

func Close

func Close(client scp.Client)

func Connect

func Connect(sshInfo SSHInfo) (scp.Client, error)

func ConnectKeyPath

func ConnectKeyPath(sshKeyPathInfo SSHKeyPathInfo) (scp.Client, error)

func ControlMcis

func ControlMcis(nsId string, mcisId string, action string) error

func ControlMcisAsync

func ControlMcisAsync(nsId string, mcisId string, action string) error

func ControlVm

func ControlVm(nsId string, mcisId string, vmId string, action string) error

func ControlVmAsync

func ControlVmAsync(wg *sync.WaitGroup, nsId string, mcisId string, vmId string, action string, results *ControlVmResultWrapper) error

func Copy

func Copy(client scp.Client, sourcePath string, remotePath string) error

func CoreDelAllMcis

func CoreDelAllMcis(nsId string) (string, error)

func CoreGetMcisAction

func CoreGetMcisAction(nsId string, mcisId string, action string) (string, error)

func CoreGetMcisVmAction

func CoreGetMcisVmAction(nsId string, mcisId string, vmId string, action string) (string, error)

func CorePostCmdMcisVm

func CorePostCmdMcisVm(nsId string, mcisId string, vmId string, req *McisCmdReq) (string, error)

func CreateMcis

func CreateMcis(nsId string, req *TbMcisReq) string

func CreateVm

func CreateVm(nsId string, mcisId string, vmInfoData *TbVmInfo) error

func DelAllMcisPolicy added in v0.2.8

func DelAllMcisPolicy(nsId string) (string, error)

func DelMcis

func DelMcis(nsId string, mcisId string) error

func DelMcisPolicy added in v0.2.8

func DelMcisPolicy(nsId string, mcisId string) error

func DelMcisVm

func DelMcisVm(nsId string, mcisId string, vmId string) error

func GetVmIp

func GetVmIp(nsId string, mcisId string, vmId string) string

func GetVmListByLabel added in v0.2.8

func GetVmListByLabel(nsId string, mcisId string, label string) ([]string, error)

func GetVmSpecId

func GetVmSpecId(nsId string, mcisId string, vmId string) string

func GetVmSshKey

func GetVmSshKey(nsId string, mcisId string, vmId string) (string, string)

func ListMcisId

func ListMcisId(nsId string) []string

func ListMcisPolicyId added in v0.2.8

func ListMcisPolicyId(nsId string) []string

func ListVmId

func ListVmId(nsId string, mcisId string) ([]string, error)

func LowerizeAndCheckMcis

func LowerizeAndCheckMcis(nsId string, mcisId string) (bool, string, error)

func LowerizeAndCheckMcisPolicy added in v0.2.8

func LowerizeAndCheckMcisPolicy(nsId string, mcisId string) (bool, string, error)

Lowerize id chars and check the same McisPolicy obj

func LowerizeAndCheckVm

func LowerizeAndCheckVm(nsId string, mcisId string, vmId string) (bool, string, error)

func OrchestrationController added in v0.2.8

func OrchestrationController()

func RunCommand

func RunCommand(client scp.Client, cmd string) (string, error)

func RunSSH

func RunSSH(vmIP string, userName string, privateKey string, cmd string) (*string, error)

func RunSSHAsync

func RunSSHAsync(wg *sync.WaitGroup, vmID string, vmIP string, userName string, privateKey string, cmd string, returnResult *[]SshCmdResult)

func SSHCopy

func SSHCopy(sshInfo SSHInfo, sourcePath string, remotePath string) error

func SSHCopyByKeyPath

func SSHCopyByKeyPath(sshInfo SSHKeyPathInfo, sourcePath string, remotePath string) error

func SSHRun

func SSHRun(sshInfo SSHInfo, cmd string) (string, error)

=============== for One Call Service

func SSHRunByKeyPath

func SSHRunByKeyPath(sshInfo SSHKeyPathInfo, cmd string) (string, error)

func UpdateMcisInfo

func UpdateMcisInfo(nsId string, mcisInfoData TbMcisInfo)

func UpdateMcisPolicyInfo added in v0.2.8

func UpdateMcisPolicyInfo(nsId string, mcisPolicyInfoData McisPolicyInfo)

func UpdateVmInfo

func UpdateVmInfo(nsId string, mcisId string, vmInfoData TbVmInfo)

func UpdateVmPublicIp

func UpdateVmPublicIp(nsId string, mcisId string, vmInfoData TbVmInfo) error

func VerifySshUserName

func VerifySshUserName(vmIp string, userNames []string, privateKey string) string

Types

type AgentInstallContent

type AgentInstallContent struct {
	Mcis_id string `json:"mcis_id"`
	Vm_id   string `json:"vm_id"`
	Vm_ip   string `json:"vm_ip"`
	Result  string `json:"result"`
}

type AgentInstallContentWrapper

type AgentInstallContentWrapper struct {
	Result_array []AgentInstallContent `json:"result_array"`
}

func InstallAgentToMcis

func InstallAgentToMcis(nsId string, mcisId string, req *McisCmdReq) (AgentInstallContentWrapper, error)

func InstallMonitorAgentToMcis

func InstallMonitorAgentToMcis(nsId string, mcisId string, req *McisCmdReq) (AgentInstallContentWrapper, error)

type AutoAction added in v0.2.8

type AutoAction struct {
	ActionType     string     `json:"actionType"`
	Vm             TbVmInfo   `json:"vm"`
	PostCommand    McisCmdReq `json:"postCommand"`
	Placement_algo string     `json:"placement_algo"`
}

type AutoCondition added in v0.2.8

type AutoCondition struct {
	Metric           string   `json:"metric"`
	Operator         string   `json:"operator"`         // <, <=, >, >=, ...
	Operand          string   `json:"operand"`          // 10, 70, 80, 98, ...
	EvaluationPeriod string   `json:"evaluationPeriod"` // evaluationPeriod
	EvaluationValue  []string `json:"evaluationValue"`
}

type BenchmarkInfo

type BenchmarkInfo struct {
	Result      string          `json:"result"`
	Unit        string          `json:"unit"`
	Desc        string          `json:"desc"`
	Elapsed     string          `json:"elapsed"`
	SpecId      string          `json:"specid"`
	ResultArray []BenchmarkInfo `json:"resultarray"` // struct-element cycle ?
}

type BenchmarkInfoArray

type BenchmarkInfoArray struct {
	ResultArray []BenchmarkInfo `json:"resultarray"`
}

func BenchmarkAction

func BenchmarkAction(nsId string, mcisId string, action string, option string) (BenchmarkInfoArray, error)

func CoreGetAllBenchmark

func CoreGetAllBenchmark(nsId string, mcisId string, host string) (*BenchmarkInfoArray, error)

func CoreGetBenchmark

func CoreGetBenchmark(nsId string, mcisId string, action string, host string) (*BenchmarkInfoArray, error)

type BenchmarkReq

type BenchmarkReq struct {
	Host string `json:"host"`
	Spec string `json:"spec"`
}

type ControlVmResult

type ControlVmResult struct {
	VmId   string `json:"vm_id"`
	Status string `json:"Status"`
	Error  error  `json:"Error"`
}

type ControlVmResultWrapper

type ControlVmResultWrapper struct {
	ResultArray []ControlVmResult `json:"resultarray"`
}

type GeoLocation

type GeoLocation struct {
	Latitude     string `json:"latitude"`
	Longitude    string `json:"longitude"`
	BriefAddr    string `json:"briefAddr"`
	CloudType    string `json:"cloudType"`
	NativeRegion string `json:"nativeRegion"`
}

func GetCloudLocation

func GetCloudLocation(cloudType string, nativeRegion string) GeoLocation

type McisCmdReq

type McisCmdReq struct {
	Mcis_id   string `json:"mcis_id"`
	Vm_id     string `json:"vm_id"`
	Ip        string `json:"ip"`
	User_name string `json:"user_name"`
	Ssh_key   string `json:"ssh_key"`
	Command   string `json:"command"`
}

type McisPolicyInfo added in v0.2.8

type McisPolicyInfo struct {
	Name   string   `json:"Name"` //MCIS Name (for request)
	Id     string   `json:"Id"`   //MCIS Id (generated ID by the Name)
	Policy []Policy `json:"policy"`

	ActionLog   string `json:"actionLog"`
	Description string `json:"description"`
}

func CreateMcisPolicy added in v0.2.8

func CreateMcisPolicy(nsId string, mcisId string, u *McisPolicyInfo) (McisPolicyInfo, error)

func GetAllMcisPolicyObject added in v0.2.8

func GetAllMcisPolicyObject(nsId string) ([]McisPolicyInfo, error)

func GetMcisPolicyObject added in v0.2.8

func GetMcisPolicyObject(nsId string, mcisId string) (McisPolicyInfo, error)

type McisRecommendReq

type McisRecommendReq struct {
	Vm_req          []TbVmRecommendReq `json:"vm_req"`
	Placement_algo  string             `json:"placement_algo"`
	Placement_param []common.KeyValue  `json:"placement_param"`
	Max_result_num  string             `json:"max_result_num"`
}

type McisStatusInfo

type McisStatusInfo struct {
	Id   string `json:"id"`
	Name string `json:"name"`
	//Vm_num string         `json:"vm_num"`
	Status       string           `json:"status"`
	TargetStatus string           `json:"targetStatus"`
	TargetAction string           `json:"targetAction"`
	Vm           []TbVmStatusInfo `json:"vm"`
	MasterVmId   string           `json:"masterVmId" example:"vm-asiaeast1-cb-01"`
	MasterIp     string           `json:"masterIp" example:"32.201.134.113"`
	// InstallMonAgent Option for CB-Dragonfly agent installation ([yes/no] default:yes)
	InstallMonAgent string `json:"installMonAgent" example:"[yes, no]"` // yes or no
}

func CoreGetMcisStatus

func CoreGetMcisStatus(nsId string, mcisId string) (*McisStatusInfo, error)

func GetMcisStatus

func GetMcisStatus(nsId string, mcisId string) (McisStatusInfo, error)

func GetMcisStatusAll added in v0.2.8

func GetMcisStatusAll(nsId string) ([]McisStatusInfo, error)

type MonAgentInstallReq

type MonAgentInstallReq struct {
	Ns_id     string `json:"ns_id,omitempty"`
	Mcis_id   string `json:"mcis_id,omitempty"`
	Vm_id     string `json:"vm_id,omitempty"`
	Public_ip string `json:"public_ip,omitempty"`
	User_name string `json:"user_name,omitempty"`
	Ssh_key   string `json:"ssh_key,omitempty"`
	Csp_type  string `json:"cspType,omitempty"`
}

type MonResultSimple added in v0.2.8

type MonResultSimple struct {
	Metric string `json:"metric"`
	VmId   string `json:"vmId"`
	Value  string `json:"value"`
	Err    string `json:"err"`
}

type MonResultSimpleResponse added in v0.2.8

type MonResultSimpleResponse struct {
	NsId           string            `json:"nsId"`
	McisId         string            `json:"mcisId"`
	McisMonitoring []MonResultSimple `json:"mcisMonitoring"`
}

func GetMonitoringData

func GetMonitoringData(nsId string, mcisId string, metric string) (MonResultSimpleResponse, error)

type MultihostBenchmarkReq

type MultihostBenchmarkReq struct {
	Multihost []BenchmarkReq `json:"multihost"`
}

type Policy added in v0.2.8

type Policy struct {
	AutoCondition AutoCondition `json:"autoCondition"`
	AutoAction    AutoAction    `json:"autoAction"`
	Status        string        `json:"status"`
}

type RegionInfo

type RegionInfo struct {
	Region string
	Zone   string
}

type SSHInfo

type SSHInfo struct {
	UserName   string // ex) "root"
	PrivateKey []byte // ex)   []byte(`-----BEGIN RSA PRIVATE KEY-----
	//              MIIEoQIBAAKCAQEArVNOLwMIp5VmZ4VPZotcoCHdEzimKalAsz+ccLfvAA1Y2ELH
	//              ...`)
	ServerPort string // ex) "node12:22"
}

====================================================================

type SSHKeyPathInfo

type SSHKeyPathInfo struct {
	UserName   string // ex) "root"
	KeyPath    string // ex) "/root/.ssh/id_rsa // You should use the full path.
	ServerPort string // ex) "node12:22"
}

====================================================================

type SpecBenchmarkInfo

type SpecBenchmarkInfo struct {
	SpecId     string `json:"specid"`
	Cpus       string `json:"cpus"`
	Cpum       string `json:"cpum"`
	MemR       string `json:"memR"`
	MemW       string `json:"memW"`
	FioR       string `json:"fioR"`
	FioW       string `json:"fioW"`
	DbR        string `json:"dbR"`
	DbW        string `json:"dbW"`
	Rtt        string `json:"rtt"`
	EvaledTime string `json:"evaledTime"`
}

type SpiderVMInfo

type SpiderVMInfo struct {
	// Fields for request
	Name               string
	ImageName          string
	VPCName            string
	SubnetName         string
	SecurityGroupNames []string
	KeyPairName        string

	// Fields for both request and response
	VMSpecName   string //  instance type or flavour, etc... ex) t2.micro or f1.micro
	VMUserId     string // ex) user1
	VMUserPasswd string

	// Fields for response
	IId               common.IID // {NameId, SystemId}
	ImageIId          common.IID
	VpcIID            common.IID
	SubnetIID         common.IID   // AWS, ex) subnet-8c4a53e4
	SecurityGroupIIds []common.IID // AWS, ex) sg-0b7452563e1121bb6
	KeyPairIId        common.IID
	StartTime         time.Time  // Timezone: based on cloud-barista server location.
	Region            RegionInfo //  ex) {us-east1, us-east1-c} or {ap-northeast-2}
	NetworkInterface  string     // ex) eth0
	PublicIP          string
	PublicDNS         string
	PrivateIP         string
	PrivateDNS        string
	VMBootDisk        string // ex) /dev/sda1
	VMBlockDisk       string // ex)
	KeyValueList      []common.KeyValue
}

type SshCmdResult

type SshCmdResult struct {
	Mcis_id string `json:"mcis_id"`
	Vm_id   string `json:"vm_id"`
	Vm_ip   string `json:"vm_ip"`
	Result  string `json:"result"`
	Err     error  `json:"err"`
}

func CorePostCmdMcis

func CorePostCmdMcis(nsId string, mcisId string, req *McisCmdReq) ([]SshCmdResult, error)

type TbMcisInfo

type TbMcisInfo struct {
	Id             string     `json:"id"`
	Name           string     `json:"name"`
	Vm             []TbVmInfo `json:"vm"`
	Placement_algo string     `json:"placement_algo"`
	Description    string     `json:"description"`
	Label          string     `json:"label"`
	Status         string     `json:"status"`
	TargetStatus   string     `json:"targetStatus"`
	TargetAction   string     `json:"targetAction"`
	// InstallMonAgent Option for CB-Dragonfly agent installation ([yes/no] default:yes)
	InstallMonAgent string `json:"installMonAgent" example:"[yes, no]"` // yes or no

}

func CoreGetAllMcis

func CoreGetAllMcis(nsId string, option string) ([]TbMcisInfo, error)

func CoreGetMcisInfo

func CoreGetMcisInfo(nsId string, mcisId string) (*TbMcisInfo, error)

func CorePostMcis

func CorePostMcis(nsId string, req *TbMcisReq) (*TbMcisInfo, error)

func GetMcisObject

func GetMcisObject(nsId string, mcisId string) (TbMcisInfo, error)

type TbMcisReq

type TbMcisReq struct {
	Name           string    `json:"name"`
	Vm             []TbVmReq `json:"vm"`
	Placement_algo string    `json:"placement_algo"`

	// InstallMonAgent Option for CB-Dragonfly agent installation ([yes/no] default:yes)
	InstallMonAgent string `json:"installMonAgent" example:"[yes, no]"` // yes or no

	Description string `json:"description"`
	Label       string `json:"label"`
}

type TbVmInfo

type TbVmInfo struct {
	Id               string   `json:"id"`
	Name             string   `json:"name"`
	ConnectionName   string   `json:"connectionName"`
	SpecId           string   `json:"specId"`
	ImageId          string   `json:"imageId"`
	VNetId           string   `json:"vNetId"`
	SubnetId         string   `json:"subnetId"`
	SecurityGroupIds []string `json:"securityGroupIds"`
	SshKeyId         string   `json:"sshKeyId"`
	VmUserAccount    string   `json:"vmUserAccount"`
	VmUserPassword   string   `json:"vmUserPassword"`
	Description      string   `json:"description"`
	Label            string   `json:"label"`

	Location GeoLocation `json:"location"`

	// 2. Provided by CB-Spider
	Region      RegionInfo `json:"region"` // AWS, ex) {us-east1, us-east1-c} or {ap-northeast-2}
	PublicIP    string     `json:"publicIP"`
	PublicDNS   string     `json:"publicDNS"`
	PrivateIP   string     `json:"privateIP"`
	PrivateDNS  string     `json:"privateDNS"`
	VMBootDisk  string     `json:"vmBootDisk"` // ex) /dev/sda1
	VMBlockDisk string     `json:"vmBlockDisk"`

	// 3. Required by CB-Tumblebug
	Status       string `json:"status"`
	TargetStatus string `json:"targetStatus"`
	TargetAction string `json:"targetAction"`

	// Montoring agent status
	MonAgentStatus string `json:"monAgentStatus" example:"[installed, notInstalled, failed]"` // yes or no// installed, notInstalled, failed

	CspViewVmDetail SpiderVMInfo `json:"cspViewVmDetail"`
}

func CoreGetMcisVmInfo

func CoreGetMcisVmInfo(nsId string, mcisId string, vmId string) (*TbVmInfo, error)

func CorePostMcisVm

func CorePostMcisVm(nsId string, mcisId string, vmInfoData *TbVmInfo) (*TbVmInfo, error)

func GetVmObject

func GetVmObject(nsId string, mcisId string, vmId string) (TbVmInfo, error)

func GetVmTemplate added in v0.2.8

func GetVmTemplate(nsId string, mcisId string, algo string) (TbVmInfo, error)

type TbVmPriority

type TbVmPriority struct {
	Priority string          `json:"priority"`
	Vm_spec  mcir.TbSpecInfo `json:"vm_spec"`
}

func GetRecommendList

func GetRecommendList(nsId string, cpuSize string, memSize string, diskSize string) ([]TbVmPriority, error)

// Info manage for MCIS recommandation

type TbVmRecommendInfo

type TbVmRecommendInfo struct {
	Vm_req          TbVmRecommendReq  `json:"vm_req"`
	Vm_priority     []TbVmPriority    `json:"vm_priority"`
	Placement_algo  string            `json:"placement_algo"`
	Placement_param []common.KeyValue `json:"placement_param"`
}

func CorePostMcisRecommand

func CorePostMcisRecommand(nsId string, req *McisRecommendReq) ([]TbVmRecommendInfo, error)

type TbVmRecommendReq

type TbVmRecommendReq struct {
	Request_name   string `json:"request_name"`
	Max_result_num string `json:"max_result_num"`

	Vcpu_size   string `json:"vcpu_size"`
	Memory_size string `json:"memory_size"`
	Disk_size   string `json:"disk_size"`

	Placement_algo  string            `json:"placement_algo"`
	Placement_param []common.KeyValue `json:"placement_param"`
}

type TbVmReq

type TbVmReq struct {
	Name             string   `json:"name"`
	ConnectionName   string   `json:"connectionName"`
	SpecId           string   `json:"specId"`
	ImageId          string   `json:"imageId"`
	VNetId           string   `json:"vNetId"`
	SubnetId         string   `json:"subnetId"`
	SecurityGroupIds []string `json:"securityGroupIds"`
	SshKeyId         string   `json:"sshKeyId"`
	VmUserAccount    string   `json:"vmUserAccount"`
	VmUserPassword   string   `json:"vmUserPassword"`
	Description      string   `json:"description"`
	Label            string   `json:"label"`
}

type TbVmStatusInfo

type TbVmStatusInfo struct {
	Id            string      `json:"id"`
	Csp_vm_id     string      `json:"csp_vm_id"`
	Name          string      `json:"name"`
	Status        string      `json:"status"`
	TargetStatus  string      `json:"targetStatus"`
	TargetAction  string      `json:"targetAction"`
	Native_status string      `json:"native_status"`
	Public_ip     string      `json:"public_ip"`
	Location      GeoLocation `json:"location"`
	// Montoring agent status
	MonAgentStatus string `json:"monAgentStatus" example:"[installed, notInstalled, failed]"` // yes or no// installed, notInstalled, failed
}

func CoreGetMcisVmStatus

func CoreGetMcisVmStatus(nsId string, mcisId string, vmId string) (*TbVmStatusInfo, error)

func GetVmCurrentPublicIp

func GetVmCurrentPublicIp(nsId string, mcisId string, vmId string) (TbVmStatusInfo, error)

func GetVmStatus

func GetVmStatus(nsId string, mcisId string, vmId string) (TbVmStatusInfo, error)

type VMStatus

type VMStatus string // Spider

GO do not support Enum. So, define like this.

const (
	Creating VMStatus = "Creating" // from launch to running
	Running  VMStatus = "Running"

	Suspending VMStatus = "Suspending" // from running to suspended
	Suspended  VMStatus = "Suspended"
	Resuming   VMStatus = "Resuming" // from suspended to running

	Rebooting VMStatus = "Rebooting" // from running to running

	Terminating VMStatus = "Terminating" // from running, suspended to terminated
	Terminated  VMStatus = "Terminated"
	NotExist    VMStatus = "NotExist" // VM does not exist

	Failed VMStatus = "Failed"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL