diff --git a/hack/python-sdk/gen-sdk.sh b/hack/python-sdk/gen-sdk.sh new file mode 100755 index 000000000..698f68257 --- /dev/null +++ b/hack/python-sdk/gen-sdk.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# Copyright 2019 The Kubeflow Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -o errexit +set -o nounset +set -o pipefail + +SWAGGER_JAR_URL="http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/2.4.6/swagger-codegen-cli-2.4.6.jar" +SWAGGER_CODEGEN_JAR="hack/python-sdk/swagger-codegen-cli.jar" +SWAGGER_CODEGEN_CONF="hack/python-sdk/swagger_config.json" +SWAGGER_CODEGEN_FILE="pkg/apis/pytorch/v1/swagger.json" +SDK_OUTPUT_PATH="sdk/python" + +if [ -z "${GOPATH:-}" ]; then + export GOPATH=$(go env GOPATH) +fi + +echo "Generating OpenAPI specification ..." +go run vendor/k8s.io/code-generator/cmd/openapi-gen/main.go --input-dirs github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/v1,github.com/kubeflow/common/job_controller/api/v1 --output-package github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/v1 --go-header-file hack/boilerplate/boilerplate.go.txt + +echo "Generating swagger file ..." +go run hack/python-sdk/main.go 0.1 > ${SWAGGER_CODEGEN_FILE} + +echo "Downloading the swagger-codegen JAR package ..." +wget -O ${SWAGGER_CODEGEN_JAR} ${SWAGGER_JAR_URL} + +echo "Generating Python SDK for Kubeflow PyTorch-Operator ..." +java -jar ${SWAGGER_CODEGEN_JAR} generate -i ${SWAGGER_CODEGEN_FILE} -l python -o ${SDK_OUTPUT_PATH} -c ${SWAGGER_CODEGEN_CONF} + +echo "Kubeflow PyTorch Operator Python SDK is generated successfully to folder ${SDK_OUTPUT_PATH}/." diff --git a/hack/python-sdk/main.go b/hack/python-sdk/main.go new file mode 100644 index 000000000..4e28f5167 --- /dev/null +++ b/hack/python-sdk/main.go @@ -0,0 +1,79 @@ +/* +Copyright 2019 kubeflow.org. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/go-openapi/spec" + pytorchjob "github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/v1" + "k8s.io/klog" + "k8s.io/kube-openapi/pkg/common" +) + +// Generate OpenAPI spec definitions for PyTorchJob Resource +func main() { + if len(os.Args) <= 1 { + klog.Fatal("Supply a version") + } + version := os.Args[1] + if !strings.HasPrefix(version, "v") { + version = "v" + version + } + oAPIDefs := pytorchjob.GetOpenAPIDefinitions(func(name string) spec.Ref { + return spec.MustCreateRef("#/definitions/" + common.EscapeJsonPointer(swaggify(name))) + }) + defs := spec.Definitions{} + for defName, val := range oAPIDefs { + defs[swaggify(defName)] = val.Schema + } + swagger := spec.Swagger{ + SwaggerProps: spec.SwaggerProps{ + Swagger: "2.0", + Definitions: defs, + Paths: &spec.Paths{Paths: map[string]spec.PathItem{}}, + Info: &spec.Info{ + InfoProps: spec.InfoProps{ + Title: "pytorch", + Description: "Python SDK for PyTorch-Operator", + Version: version, + }, + }, + }, + } + jsonBytes, err := json.MarshalIndent(swagger, "", " ") + if err != nil { + klog.Fatal(err.Error()) + } + fmt.Println(string(jsonBytes)) +} + +func swaggify(name string) string { + name = strings.Replace(name, "github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/", "", -1) + name = strings.Replace(name, "github.com/kubeflow/common/job_controller/api/", "", -1) + name = strings.Replace(name, "github.com/kubernetes-sigs/kube-batch/pkg/client/clientset/", "", -1) + name = strings.Replace(name, "k8s.io/api/core/", "", -1) + name = strings.Replace(name, "k8s.io/apimachinery/pkg/apis/meta/", "", -1) + name = strings.Replace(name, "k8s.io/kubernetes/pkg/controller/", "", -1) + name = strings.Replace(name, "k8s.io/client-go/listers/core/", "", -1) + name = strings.Replace(name, "k8s.io/client-go/util/workqueue", "", -1) + name = strings.Replace(name, "/", ".", -1) + return name +} diff --git a/hack/python-sdk/swagger_config.json b/hack/python-sdk/swagger_config.json new file mode 100644 index 000000000..fc3ab884c --- /dev/null +++ b/hack/python-sdk/swagger_config.json @@ -0,0 +1,13 @@ +{ + "packageName" : "pytorchjob", + "projectName" : "pytorchjob", + "packageVersion": "0.1", + "importMappings": { + "V1Container": "from kubernetes.client import V1Container", + "V1ObjectMeta": "from kubernetes.client import V1ObjectMeta", + "V1ListMeta": "from kubernetes.client import V1ListMeta", + "V1ResourceRequirements": "from kubernetes.client import V1ResourceRequirements", + "V1JobCondition": "from kubernetes.client import V1JobCondition", + "V1PodTemplateSpec": "from kubernetes.client import V1PodTemplateSpec" + } +} diff --git a/pkg/apis/pytorch/v1/doc.go b/pkg/apis/pytorch/v1/doc.go index 9649a4e75..48b85e8c0 100644 --- a/pkg/apis/pytorch/v1/doc.go +++ b/pkg/apis/pytorch/v1/doc.go @@ -14,6 +14,7 @@ // +k8s:deepcopy-gen=package,register // +k8s:defaulter-gen=TypeMeta +// +k8s:openapi-gen=true // Package v1 is the v1 version of the API. // +groupName=kubeflow.org diff --git a/pkg/apis/pytorch/v1/openapi_generated.go b/pkg/apis/pytorch/v1/openapi_generated.go new file mode 100644 index 000000000..87f7701aa --- /dev/null +++ b/pkg/apis/pytorch/v1/openapi_generated.go @@ -0,0 +1,342 @@ +// +build !ignore_autogenerated + +// Copyright 2019 The Kubeflow Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by main. DO NOT EDIT. + +// This file was autogenerated by openapi-gen. Do not edit it manually! + +package v1 + +import ( + spec "github.com/go-openapi/spec" + common "k8s.io/kube-openapi/pkg/common" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{ + "github.com/kubeflow/common/job_controller/api/v1.JobCondition": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "JobCondition describes the state of the job at a certain point.", + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of job condition.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "The reason for the condition's last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human readable message indicating details about the transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "lastUpdateTime": { + SchemaProps: spec.SchemaProps{ + Description: "The last time this condition was updated.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time the condition transitioned from one status to another.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + }, + "github.com/kubeflow/common/job_controller/api/v1.JobStatus": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "JobStatus represents the current observed state of the training Job.", + Properties: map[string]spec.Schema{ + "conditions": { + SchemaProps: spec.SchemaProps{ + Description: "Conditions is an array of current observed job conditions.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/kubeflow/common/job_controller/api/v1.JobCondition"), + }, + }, + }, + }, + }, + "replicaStatuses": { + SchemaProps: spec.SchemaProps{ + Description: "ReplicaStatuses is map of ReplicaType and ReplicaStatus, specifies the status of each replica.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/kubeflow/common/job_controller/api/v1.ReplicaStatus"), + }, + }, + }, + }, + }, + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "Represents time when the job was acknowledged by the job controller. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "completionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastReconcileTime": { + SchemaProps: spec.SchemaProps{ + Description: "Represents last time when the job was reconciled. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + }, + Required: []string{"conditions", "replicaStatuses"}, + }, + }, + Dependencies: []string{ + "github.com/kubeflow/common/job_controller/api/v1.JobCondition", "github.com/kubeflow/common/job_controller/api/v1.ReplicaStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + }, + "github.com/kubeflow/common/job_controller/api/v1.ReplicaSpec": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicaSpec is a description of the replica", + Properties: map[string]spec.Schema{ + "replicas": { + SchemaProps: spec.SchemaProps{ + Description: "Replicas is the desired number of replicas of the given template. If unspecified, defaults to 1.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Description: "Template is the object that describes the pod that will be created for this replica. RestartPolicy in PodTemplateSpec will be overide by RestartPolicy in ReplicaSpec", + Ref: ref("k8s.io/api/core/v1.PodTemplateSpec"), + }, + }, + "restartPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Restart policy for all replicas within the job. One of Always, OnFailure, Never and ExitCode. Default to Never.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodTemplateSpec"}, + }, + "github.com/kubeflow/common/job_controller/api/v1.ReplicaStatus": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicaStatus represents the current observed state of the replica.", + Properties: map[string]spec.Schema{ + "active": { + SchemaProps: spec.SchemaProps{ + Description: "The number of actively running pods.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "succeeded": { + SchemaProps: spec.SchemaProps{ + Description: "The number of pods which reached phase Succeeded.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "failed": { + SchemaProps: spec.SchemaProps{ + Description: "The number of pods which reached phase Failed.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + Dependencies: []string{}, + }, + "github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/v1.PyTorchJob": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a PyTorchJob resource.", + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard Kubernetes object's metadata.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Specification of the desired state of the PyTorchJob.", + Ref: ref("github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/v1.PyTorchJobSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Most recently observed status of the PyTorchJob. Read-only (modified by the system).", + Ref: ref("github.com/kubeflow/common/job_controller/api/v1.JobStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/kubeflow/common/job_controller/api/v1.JobStatus", "github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/v1.PyTorchJobSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + }, + "github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/v1.PyTorchJobList": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PyTorchJobList is a list of PyTorchJobs.", + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of PyTorchJobs.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/v1.PyTorchJob"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/v1.PyTorchJob", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + }, + "github.com/kubeflow/pytorch-operator/pkg/apis/pytorch/v1.PyTorchJobSpec": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PyTorchJobSpec is a desired state description of the PyTorchJob.", + Properties: map[string]spec.Schema{ + "activeDeadlineSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the duration (in seconds) since startTime during which the job can remain active before it is terminated. Must be a positive integer. This setting applies only to pods where restartPolicy is OnFailure or Always.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "backoffLimit": { + SchemaProps: spec.SchemaProps{ + Description: "Number of retries before marking this job as failed.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "cleanPodPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Defines the policy for cleaning up pods after the PyTorchJob completes. Defaults to None.", + Type: []string{"string"}, + Format: "", + }, + }, + "ttlSecondsAfterFinished": { + SchemaProps: spec.SchemaProps{ + Description: "Defines the TTL for cleaning up finished PyTorchJobs (temporary before Kubernetes adds the cleanup controller). It may take extra ReconcilePeriod seconds for the cleanup, since reconcile gets called periodically. Defaults to infinite.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "pytorchReplicaSpecs": { + SchemaProps: spec.SchemaProps{ + Description: "A map of PyTorchReplicaType (type) to ReplicaSpec (value). Specifies the PyTorch cluster configuration. For example,\n {\n \"Master\": PyTorchReplicaSpec,\n \"Worker\": PyTorchReplicaSpec,\n }", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/kubeflow/common/job_controller/api/v1.ReplicaSpec"), + }, + }, + }, + }, + }, + }, + Required: []string{"pytorchReplicaSpecs"}, + }, + }, + Dependencies: []string{ + "github.com/kubeflow/common/job_controller/api/v1.ReplicaSpec"}, + }, + } +} diff --git a/sdk/python/.gitignore b/sdk/python/.gitignore new file mode 100644 index 000000000..132d1c359 --- /dev/null +++ b/sdk/python/.gitignore @@ -0,0 +1,72 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ +venv/ +.python-version + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +#Ipython Notebook +.ipynb_checkpoints + +# Add more useless files +tox.ini +test-requirements.txt +git_push.sh +.travis.yml +.swagger-codegen +.swagger-codegen-ignore diff --git a/sdk/python/OWNERS b/sdk/python/OWNERS new file mode 100644 index 000000000..c564ae86b --- /dev/null +++ b/sdk/python/OWNERS @@ -0,0 +1,2 @@ +approvers: + - jinchihe diff --git a/sdk/python/README.md b/sdk/python/README.md new file mode 100644 index 000000000..f88546237 --- /dev/null +++ b/sdk/python/README.md @@ -0,0 +1,56 @@ +# Kubeflow PyTorchJob SDK +Python SDK for PyTorch-Operator + +## Requirements. + +Python 2.7 and 3.5+ + +## Installation & Usage +### pip install + +```sh +pip install kubeflow-pytorchjob +``` + +Then import the package: +```python +from kubeflow import pytorchjob +``` + +### Setuptools + +Install via [Setuptools](http://pypi.python.org/pypi/setuptools). + +```sh +python setup.py install --user +``` +(or `sudo python setup.py install` to install the package for all users) + + +## Getting Started + +Please follow the [sample](../../examples/sdk/pytorchjob-sdk.ipynb) to create, update and delete PyTorchJob. + +## Documentation for API Endpoints + +Class | Method | Description +------------ | ------------- | ------------- | ------------- +[PyTorchJobClient](docs/PyTorchJobClient.md) | [create](docs/PyTorchJobClient.md#create) | Create PyTorchJob| +[PyTorchJobClient](docs/PyTorchJobClient.md) | [get](docs/PyTorchJobClient.md#get) | Get the specified PyTorchJob or all PyTorchJob in the namespace | +[PyTorchJobClient](docs/PyTorchJobClient.md) | [patch](docs/PyTorchJobClient.md#patch) | Patch the specified PyTorchJob| +[PyTorchJobClient](docs/PyTorchJobClient.md) | [delete](docs/PyTorchJobClient.md#delete) | Delete the specified PyTorchJob | + + +## Documentation For Models + + - [V1JobCondition](docs/V1JobCondition.md) + - [V1JobStatus](docs/V1JobStatus.md) + - [V1PyTorchJob](docs/V1PyTorchJob.md) + - [V1PyTorchJobList](docs/V1PyTorchJobList.md) + - [V1PyTorchJobSpec](docs/V1PyTorchJobSpec.md) + - [V1ReplicaSpec](docs/V1ReplicaSpec.md) + - [V1ReplicaStatus](docs/V1ReplicaStatus.md) + + + + diff --git a/sdk/python/docs/PyTorchJobClient.md b/sdk/python/docs/PyTorchJobClient.md new file mode 100644 index 000000000..84bd717e9 --- /dev/null +++ b/sdk/python/docs/PyTorchJobClient.md @@ -0,0 +1,138 @@ +# PyTorchJobClient + +> PyTorchJobClient(config_file=None, context=None, client_configuration=None, persist_config=True) + +User can loads authentication and cluster information from kube-config file and stores them in kubernetes.client.configuration. Parameters are as following: + +parameter | Description +------------ | ------------- +config_file | Name of the kube-config file. Defaults to `~/.kube/config`. Note that for the case that the SDK is running in cluster and you want to operate PyTorchJob in another remote cluster, user must set `config_file` to load kube-config file explicitly, e.g. `PyTorchJobClient(config_file="~/.kube/config")`. | +context |Set the active context. If is set to None, current_context from config file will be used.| +client_configuration | The kubernetes.client.Configuration to set configs to.| +persist_config | If True, config file will be updated when changed (e.g GCP token refresh).| + + +The APIs for PyTorchJobClient are as following: + +Class | Method | Description +------------ | ------------- | ------------- +PyTorchJobClient| [create](#create) | Create PyTorchJob| +PyTorchJobClient | [get](#get) | Get the specified PyTorchJob or all PyTorchJob in the namespace | +PyTorchJobClient | [patch](#patch) | Patch the specified PyTorchJob| +PyTorchJobClient | [delete](#delete) | Delete the specified PyTorchJob | + + +## create +> create(ptorchjob, namespace=None) + +Create the provided ptorchjob in the specified namespace + +### Example + +```python +from kubernetes.client import V1PodTemplateSpec +from kubernetes.client import V1ObjectMeta +from kubernetes.client import V1PodSpec +from kubernetes.client import V1Container + +from kubeflow.ptorchjob import constants +from kubeflow.ptorchjob import utils +from kubeflow.ptorchjob import V1ReplicaSpec +from kubeflow.ptorchjob import V1PyTorchJob +from kubeflow.ptorchjob import V1PyTorchJobList +from kubeflow.ptorchjob import V1PyTorchJobSpec +from kubeflow.ptorchjob import PyTorchJobClient + +ptorchjob_client = PyTorchJobClient() +ptorchjob_client.create(ptorchjob) + +``` + + +### Parameters +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +ptorchjob | [V1PyTorchJob](V1PyTorchJob.md) | ptorchjob defination| Required | +namespace | str | Namespace for ptorchjob deploying to. If the `namespace` is not defined, will align with ptorchjob definition, or use current or default namespace if namespace is not specified in ptorchjob definition. | Optional | + +### Return type +object + +## get +> get(name=None, namespace=None) + +Get the created ptorchjob in the specified namespace + +### Example + +```python +from kubeflow.ptorchjob import PyTorchJobClient + +ptorchjob_client = PyTorchJobClient() +ptorchjob_client.get('mnist', namespace='kubeflow') +``` + +### Parameters +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +name | str | ptorchjob name. If the `name` is not specified, it will get all ptorchjobs in the namespace.| Optional. | +namespace | str | The ptorchjob's namespace. Defaults to current or default namespace.| Optional | + + +### Return type +object + + +## patch +> patch(name, ptorchjob, namespace=None) + +Patch the created ptorchjob in the specified namespace. + +Note that if you want to set the field from existing value to `None`, `patch` API may not work, you need to use [replace](#replace) API to remove the field value. + +### Example + +```python + +ptorchjob = V1PyTorchJob( + api_version="kubeflow.org/v1", + ... #update something in PyTorchJob spec +) + +ptorchjob_client = PyTorchJobClient() +ptorchjob_client.patch('mnist', isvc) + +``` + +### Parameters +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +ptorchjob | [V1PyTorchJob](V1PyTorchJob.md) | ptorchjob defination| Required | +namespace | str | The ptorchjob's namespace for patching. If the `namespace` is not defined, will align with ptorchjob definition, or use current or default namespace if namespace is not specified in ptorchjob definition. | Optional| + +### Return type +object + + +## delete +> delete(name, namespace=None) + +Delete the created ptorchjob in the specified namespace + +### Example + +```python +from kubeflow.ptorchjob import PyTorchJobClient + +ptorchjob_client = PyTorchJobClient() +ptorchjob_client.delete('mnist', namespace='kubeflow') +``` + +### Parameters +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +name | str | ptorchjob name| | +namespace | str | The ptorchjob's namespace. Defaults to current or default namespace. | Optional| + +### Return type +object diff --git a/sdk/python/docs/V1JobCondition.md b/sdk/python/docs/V1JobCondition.md new file mode 100644 index 000000000..2202f464f --- /dev/null +++ b/sdk/python/docs/V1JobCondition.md @@ -0,0 +1,15 @@ +# V1JobCondition + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**last_transition_time** | [**V1Time**](V1Time.md) | Last time the condition transitioned from one status to another. | [optional] +**last_update_time** | [**V1Time**](V1Time.md) | The last time this condition was updated. | [optional] +**message** | **str** | A human readable message indicating details about the transition. | [optional] +**reason** | **str** | The reason for the condition's last transition. | [optional] +**status** | **str** | Status of the condition, one of True, False, Unknown. | +**type** | **str** | Type of job condition. | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/python/docs/V1JobStatus.md b/sdk/python/docs/V1JobStatus.md new file mode 100644 index 000000000..4d027c437 --- /dev/null +++ b/sdk/python/docs/V1JobStatus.md @@ -0,0 +1,14 @@ +# V1JobStatus + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**completion_time** | [**V1Time**](V1Time.md) | Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. | [optional] +**conditions** | [**list[V1JobCondition]**](V1JobCondition.md) | Conditions is an array of current observed job conditions. | +**last_reconcile_time** | [**V1Time**](V1Time.md) | Represents last time when the job was reconciled. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. | [optional] +**replica_statuses** | [**dict(str, V1ReplicaStatus)**](V1ReplicaStatus.md) | ReplicaStatuses is map of ReplicaType and ReplicaStatus, specifies the status of each replica. | +**start_time** | [**V1Time**](V1Time.md) | Represents time when the job was acknowledged by the job controller. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/python/docs/V1PyTorchJob.md b/sdk/python/docs/V1PyTorchJob.md new file mode 100644 index 000000000..7f06eb27e --- /dev/null +++ b/sdk/python/docs/V1PyTorchJob.md @@ -0,0 +1,14 @@ +# V1PyTorchJob + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**api_version** | **str** | APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources | [optional] +**kind** | **str** | Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds | [optional] +**metadata** | [**V1ObjectMeta**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1ObjectMeta.md) | Standard Kubernetes object's metadata. | [optional] +**spec** | [**V1PyTorchJobSpec**](V1PyTorchJobSpec.md) | Specification of the desired state of the PyTorchJob. | [optional] +**status** | [**V1JobStatus**](V1JobStatus.md) | Most recently observed status of the PyTorchJob. Read-only (modified by the system). | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/python/docs/V1PyTorchJobList.md b/sdk/python/docs/V1PyTorchJobList.md new file mode 100644 index 000000000..320f40534 --- /dev/null +++ b/sdk/python/docs/V1PyTorchJobList.md @@ -0,0 +1,13 @@ +# V1PyTorchJobList + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**api_version** | **str** | APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources | [optional] +**items** | [**list[V1PyTorchJob]**](V1PyTorchJob.md) | List of PyTorchJobs. | +**kind** | **str** | Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds | [optional] +**metadata** | [**V1ListMeta**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1ListMeta.md) | Standard list metadata. | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/python/docs/V1PyTorchJobSpec.md b/sdk/python/docs/V1PyTorchJobSpec.md new file mode 100644 index 000000000..45b5470b9 --- /dev/null +++ b/sdk/python/docs/V1PyTorchJobSpec.md @@ -0,0 +1,14 @@ +# V1PyTorchJobSpec + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**active_deadline_seconds** | **int** | Specifies the duration (in seconds) since startTime during which the job can remain active before it is terminated. Must be a positive integer. This setting applies only to pods where restartPolicy is OnFailure or Always. | [optional] +**backoff_limit** | **int** | Number of retries before marking this job as failed. | [optional] +**clean_pod_policy** | **str** | Defines the policy for cleaning up pods after the PyTorchJob completes. Defaults to None. | [optional] +**pytorch_replica_specs** | [**dict(str, V1ReplicaSpec)**](V1ReplicaSpec.md) | A map of PyTorchReplicaType (type) to ReplicaSpec (value). Specifies the PyTorch cluster configuration. For example, { \"Master\": PyTorchReplicaSpec, \"Worker\": PyTorchReplicaSpec, } | +**ttl_seconds_after_finished** | **int** | Defines the TTL for cleaning up finished PyTorchJobs (temporary before Kubernetes adds the cleanup controller). It may take extra ReconcilePeriod seconds for the cleanup, since reconcile gets called periodically. Defaults to infinite. | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/python/docs/V1ReplicaSpec.md b/sdk/python/docs/V1ReplicaSpec.md new file mode 100644 index 000000000..6f9faf0c7 --- /dev/null +++ b/sdk/python/docs/V1ReplicaSpec.md @@ -0,0 +1,12 @@ +# V1ReplicaSpec + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**replicas** | **int** | Replicas is the desired number of replicas of the given template. If unspecified, defaults to 1. | [optional] +**restart_policy** | **str** | Restart policy for all replicas within the job. One of Always, OnFailure, Never and ExitCode. Default to Never. | [optional] +**template** | [**V1PodTemplateSpec**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodTemplateSpec.md) | Template is the object that describes the pod that will be created for this replica. RestartPolicy in PodTemplateSpec will be overide by RestartPolicy in ReplicaSpec | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/python/docs/V1ReplicaStatus.md b/sdk/python/docs/V1ReplicaStatus.md new file mode 100644 index 000000000..847e365eb --- /dev/null +++ b/sdk/python/docs/V1ReplicaStatus.md @@ -0,0 +1,12 @@ +# V1ReplicaStatus + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**active** | **int** | The number of actively running pods. | [optional] +**failed** | **int** | The number of pods which reached phase Failed. | [optional] +**succeeded** | **int** | The number of pods which reached phase Succeeded. | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/python/docs/V1Time.md b/sdk/python/docs/V1Time.md new file mode 100644 index 000000000..63a5fb595 --- /dev/null +++ b/sdk/python/docs/V1Time.md @@ -0,0 +1,9 @@ +# V1Time + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/python/kubeflow/__init__.py b/sdk/python/kubeflow/__init__.py new file mode 100644 index 000000000..69e3be50d --- /dev/null +++ b/sdk/python/kubeflow/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/sdk/python/kubeflow/pytorchjob/__init__.py b/sdk/python/kubeflow/pytorchjob/__init__.py new file mode 100644 index 000000000..63cb19773 --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/__init__.py @@ -0,0 +1,30 @@ +# coding: utf-8 + +# flake8: noqa + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +# import apis into sdk package + +# import ApiClient +from pytorchjob.api_client import ApiClient +from pytorchjob.configuration import Configuration +# import models into sdk package +from pytorchjob.models.v1_job_condition import V1JobCondition +from pytorchjob.models.v1_job_status import V1JobStatus +from pytorchjob.models.v1_py_torch_job import V1PyTorchJob +from pytorchjob.models.v1_py_torch_job_list import V1PyTorchJobList +from pytorchjob.models.v1_py_torch_job_spec import V1PyTorchJobSpec +from pytorchjob.models.v1_replica_spec import V1ReplicaSpec +from pytorchjob.models.v1_replica_status import V1ReplicaStatus diff --git a/sdk/python/kubeflow/pytorchjob/api/__init__.py b/sdk/python/kubeflow/pytorchjob/api/__init__.py new file mode 100644 index 000000000..36dce7fe2 --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/api/__init__.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import + +# flake8: noqa + +# import apis into api package diff --git a/sdk/python/kubeflow/pytorchjob/api/py_torch_job_client.py b/sdk/python/kubeflow/pytorchjob/api/py_torch_job_client.py new file mode 100644 index 000000000..fc80185e2 --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/api/py_torch_job_client.py @@ -0,0 +1,151 @@ +# Copyright 2019 The Kubeflow Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from kubernetes import client, config + +from kubeflow.pytorchjob.constants import constants +from kubeflow.pytorchjob.utils import utils + + +class PyTorchJobClient(object): + + def __init__(self, config_file=None, context=None, # pylint: disable=too-many-arguments + client_configuration=None, persist_config=True): + """ + PyTorchJob client constructor + :param config_file: kubeconfig file, defaults to ~/.kube/config + :param context: kubernetes context + :param client_configuration: kubernetes configuration object + :param persist_config: + """ + if config_file or not utils.is_running_in_k8s(): + config.load_kube_config( + config_file=config_file, + context=context, + client_configuration=client_configuration, + persist_config=persist_config) + else: + config.load_incluster_config() + + self.api_instance = client.CustomObjectsApi() + + + def create(self, pytorchjob, namespace=None): + """ + Create the PyTorchJob + :param pytorchjob: pytorchjob object + :param namespace: defaults to current or default namespace + :return: created pytorchjob + """ + + if namespace is None: + namespace = utils.set_pytorchjob_namespace(pytorchjob) + + try: + outputs = self.api_instance.create_namespaced_custom_object( + constants.PyTorchJob_GROUP, + constants.PyTorchJob_VERSION, + namespace, + constants.PyTorchJob_PLURAL, + pytorchjob) + except client.rest.ApiException as e: + raise RuntimeError( + "Exception when calling CustomObjectsApi->create_namespaced_custom_object:\ + %s\n" % e) + + return outputs + + def get(self, name=None, namespace=None): + """ + Get the pytorchjob + :param name: existing pytorchjob name + :param namespace: defaults to current or default namespace + :return: pytorchjob + """ + if namespace is None: + namespace = utils.get_default_target_namespace() + + if name: + try: + return self.api_instance.get_namespaced_custom_object( + constants.PyTorchJob_GROUP, + constants.PyTorchJob_VERSION, + namespace, + constants.PyTorchJob_PLURAL, + name) + except client.rest.ApiException as e: + raise RuntimeError( + "Exception when calling CustomObjectsApi->get_namespaced_custom_object:\ + %s\n" % e) + else: + try: + return self.api_instance.list_namespaced_custom_object( + constants.PyTorchJob_GROUP, + constants.PyTorchJob_VERSION, + namespace, + constants.PyTorchJob_PLURAL) + except client.rest.ApiException as e: + raise RuntimeError( + "Exception when calling CustomObjectsApi->list_namespaced_custom_object:\ + %s\n" % e) + + def patch(self, name, pytorchjob, namespace=None): + """ + Patch existing pytorchjob + :param name: existing pytorchjob name + :param pytorchjob: patched pytorchjob + :param namespace: defaults to current or default namespace + :return: patched pytorchjob + """ + if namespace is None: + namespace = utils.set_pytorchjob_namespace(pytorchjob) + + try: + outputs = self.api_instance.patch_namespaced_custom_object( + constants.PyTorchJob_GROUP, + constants.PyTorchJob_VERSION, + namespace, + constants.PyTorchJob_PLURAL, + name, + pytorchjob) + except client.rest.ApiException as e: + raise RuntimeError( + "Exception when calling CustomObjectsApi->patch_namespaced_custom_object:\ + %s\n" % e) + + return outputs + + + def delete(self, name, namespace=None): + """ + Delete the pytorchjob + :param name: pytorchjob name + :param namespace: defaults to current or default namespace + :return: + """ + if namespace is None: + namespace = utils.get_default_target_namespace() + + try: + return self.api_instance.delete_namespaced_custom_object( + constants.PyTorchJob_GROUP, + constants.PyTorchJob_VERSION, + namespace, + constants.PyTorchJob_PLURAL, + name, + client.V1DeleteOptions()) + except client.rest.ApiException as e: + raise RuntimeError( + "Exception when calling CustomObjectsApi->delete_namespaced_custom_object:\ + %s\n" % e) diff --git a/sdk/python/kubeflow/pytorchjob/api_client.py b/sdk/python/kubeflow/pytorchjob/api_client.py new file mode 100644 index 000000000..3cd045b80 --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/api_client.py @@ -0,0 +1,638 @@ +# coding: utf-8 +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + +from __future__ import absolute_import + +import datetime +import json +import mimetypes +from multiprocessing.pool import ThreadPool +import os +import re +import tempfile + +# python 2 and python 3 compatibility library +import six +from six.moves.urllib.parse import quote + +from pytorchjob.configuration import Configuration +import pytorchjob.models +from pytorchjob import rest + + +class ApiClient(object): + """Generic API client for Swagger client library builds. + + Swagger generic API client. This client handles the client- + server communication, and is invariant across implementations. Specifics of + the methods and models for each application are generated from the Swagger + templates. + + NOTE: This class is auto generated by the swagger code generator program. + Ref: https://github.com/swagger-api/swagger-codegen + Do not edit the class manually. + + :param configuration: .Configuration object for this client + :param header_name: a header to pass when making calls to the API. + :param header_value: a header value to pass when making calls to + the API. + :param cookie: a cookie to include in the header when making calls + to the API + """ + + PRIMITIVE_TYPES = (float, bool, bytes, six.text_type) + six.integer_types + NATIVE_TYPES_MAPPING = { + 'int': int, + 'long': int if six.PY3 else long, # noqa: F821 + 'float': float, + 'str': str, + 'bool': bool, + 'date': datetime.date, + 'datetime': datetime.datetime, + 'object': object, + } + + def __init__(self, configuration=None, header_name=None, header_value=None, + cookie=None): + if configuration is None: + configuration = Configuration() + self.configuration = configuration + + # Use the pool property to lazily initialize the ThreadPool. + self._pool = None + self.rest_client = rest.RESTClientObject(configuration) + self.default_headers = {} + if header_name is not None: + self.default_headers[header_name] = header_value + self.cookie = cookie + # Set default User-Agent. + self.user_agent = 'Swagger-Codegen/0.1/python' + + def __del__(self): + if self._pool is not None: + self._pool.close() + self._pool.join() + + @property + def pool(self): + if self._pool is None: + self._pool = ThreadPool() + return self._pool + + @property + def user_agent(self): + """User agent for this API client""" + return self.default_headers['User-Agent'] + + @user_agent.setter + def user_agent(self, value): + self.default_headers['User-Agent'] = value + + def set_default_header(self, header_name, header_value): + self.default_headers[header_name] = header_value + + def __call_api( + self, resource_path, method, path_params=None, + query_params=None, header_params=None, body=None, post_params=None, + files=None, response_type=None, auth_settings=None, + _return_http_data_only=None, collection_formats=None, + _preload_content=True, _request_timeout=None): + + config = self.configuration + + # header parameters + header_params = header_params or {} + header_params.update(self.default_headers) + if self.cookie: + header_params['Cookie'] = self.cookie + if header_params: + header_params = self.sanitize_for_serialization(header_params) + header_params = dict(self.parameters_to_tuples(header_params, + collection_formats)) + + # path parameters + if path_params: + path_params = self.sanitize_for_serialization(path_params) + path_params = self.parameters_to_tuples(path_params, + collection_formats) + for k, v in path_params: + # specified safe chars, encode everything + resource_path = resource_path.replace( + '{%s}' % k, + quote(str(v), safe=config.safe_chars_for_path_param) + ) + + # query parameters + if query_params: + query_params = self.sanitize_for_serialization(query_params) + query_params = self.parameters_to_tuples(query_params, + collection_formats) + + # post parameters + if post_params or files: + post_params = self.prepare_post_parameters(post_params, files) + post_params = self.sanitize_for_serialization(post_params) + post_params = self.parameters_to_tuples(post_params, + collection_formats) + + # auth setting + self.update_params_for_auth(header_params, query_params, auth_settings) + + # body + if body: + body = self.sanitize_for_serialization(body) + + # request url + url = self.configuration.host + resource_path + + # perform request and return response + response_data = self.request( + method, url, query_params=query_params, headers=header_params, + post_params=post_params, body=body, + _preload_content=_preload_content, + _request_timeout=_request_timeout) + + self.last_response = response_data + + return_data = response_data + if _preload_content: + # deserialize response data + if response_type: + return_data = self.deserialize(response_data, response_type) + else: + return_data = None + + if _return_http_data_only: + return (return_data) + else: + return (return_data, response_data.status, + response_data.getheaders()) + + def sanitize_for_serialization(self, obj): + """Builds a JSON POST object. + + If obj is None, return None. + If obj is str, int, long, float, bool, return directly. + If obj is datetime.datetime, datetime.date + convert to string in iso8601 format. + If obj is list, sanitize each element in the list. + If obj is dict, return the dict. + If obj is swagger model, return the properties dict. + + :param obj: The data to serialize. + :return: The serialized form of data. + """ + if obj is None: + return None + elif isinstance(obj, self.PRIMITIVE_TYPES): + return obj + elif isinstance(obj, list): + return [self.sanitize_for_serialization(sub_obj) + for sub_obj in obj] + elif isinstance(obj, tuple): + return tuple(self.sanitize_for_serialization(sub_obj) + for sub_obj in obj) + elif isinstance(obj, (datetime.datetime, datetime.date)): + return obj.isoformat() + + if isinstance(obj, dict): + obj_dict = obj + else: + # Convert model obj to dict except + # attributes `swagger_types`, `attribute_map` + # and attributes which value is not None. + # Convert attribute name to json key in + # model definition for request. + obj_dict = {obj.attribute_map[attr]: getattr(obj, attr) + for attr, _ in six.iteritems(obj.swagger_types) + if getattr(obj, attr) is not None} + + return {key: self.sanitize_for_serialization(val) + for key, val in six.iteritems(obj_dict)} + + def deserialize(self, response, response_type): + """Deserializes response into an object. + + :param response: RESTResponse object to be deserialized. + :param response_type: class literal for + deserialized object, or string of class name. + + :return: deserialized object. + """ + # handle file downloading + # save response body into a tmp file and return the instance + if response_type == "file": + return self.__deserialize_file(response) + + # fetch data from response object + try: + data = json.loads(response.data) + except ValueError: + data = response.data + + return self.__deserialize(data, response_type) + + def __deserialize(self, data, klass): + """Deserializes dict, list, str into an object. + + :param data: dict, list or str. + :param klass: class literal, or string of class name. + + :return: object. + """ + if data is None: + return None + + if type(klass) == str: + if klass.startswith('list['): + sub_kls = re.match(r'list\[(.*)\]', klass).group(1) + return [self.__deserialize(sub_data, sub_kls) + for sub_data in data] + + if klass.startswith('dict('): + sub_kls = re.match(r'dict\(([^,]*), (.*)\)', klass).group(2) + return {k: self.__deserialize(v, sub_kls) + for k, v in six.iteritems(data)} + + # convert str to class + if klass in self.NATIVE_TYPES_MAPPING: + klass = self.NATIVE_TYPES_MAPPING[klass] + else: + klass = getattr(pytorchjob.models, klass) + + if klass in self.PRIMITIVE_TYPES: + return self.__deserialize_primitive(data, klass) + elif klass == object: + return self.__deserialize_object(data) + elif klass == datetime.date: + return self.__deserialize_date(data) + elif klass == datetime.datetime: + return self.__deserialize_datatime(data) + else: + return self.__deserialize_model(data, klass) + + def call_api(self, resource_path, method, + path_params=None, query_params=None, header_params=None, + body=None, post_params=None, files=None, + response_type=None, auth_settings=None, async_req=None, + _return_http_data_only=None, collection_formats=None, + _preload_content=True, _request_timeout=None): + """Makes the HTTP request (synchronous) and returns deserialized data. + + To make an async request, set the async_req parameter. + + :param resource_path: Path to method endpoint. + :param method: Method to call. + :param path_params: Path parameters in the url. + :param query_params: Query parameters in the url. + :param header_params: Header parameters to be + placed in the request header. + :param body: Request body. + :param post_params dict: Request post form parameters, + for `application/x-www-form-urlencoded`, `multipart/form-data`. + :param auth_settings list: Auth Settings names for the request. + :param response: Response data type. + :param files dict: key -> filename, value -> filepath, + for `multipart/form-data`. + :param async_req bool: execute request asynchronously + :param _return_http_data_only: response data without head status code + and headers + :param collection_formats: dict of collection formats for path, query, + header, and post parameters. + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: + If async_req parameter is True, + the request will be called asynchronously. + The method will return the request thread. + If parameter async_req is False or missing, + then the method will return the response directly. + """ + if not async_req: + return self.__call_api(resource_path, method, + path_params, query_params, header_params, + body, post_params, files, + response_type, auth_settings, + _return_http_data_only, collection_formats, + _preload_content, _request_timeout) + else: + thread = self.pool.apply_async(self.__call_api, (resource_path, + method, path_params, query_params, + header_params, body, + post_params, files, + response_type, auth_settings, + _return_http_data_only, + collection_formats, + _preload_content, _request_timeout)) + return thread + + def request(self, method, url, query_params=None, headers=None, + post_params=None, body=None, _preload_content=True, + _request_timeout=None): + """Makes the HTTP request using RESTClient.""" + if method == "GET": + return self.rest_client.GET(url, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + headers=headers) + elif method == "HEAD": + return self.rest_client.HEAD(url, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + headers=headers) + elif method == "OPTIONS": + return self.rest_client.OPTIONS(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "POST": + return self.rest_client.POST(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "PUT": + return self.rest_client.PUT(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "PATCH": + return self.rest_client.PATCH(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "DELETE": + return self.rest_client.DELETE(url, + query_params=query_params, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + else: + raise ValueError( + "http method must be `GET`, `HEAD`, `OPTIONS`," + " `POST`, `PATCH`, `PUT` or `DELETE`." + ) + + def parameters_to_tuples(self, params, collection_formats): + """Get parameters as list of tuples, formatting collections. + + :param params: Parameters as dict or list of two-tuples + :param dict collection_formats: Parameter collection formats + :return: Parameters as list of tuples, collections formatted + """ + new_params = [] + if collection_formats is None: + collection_formats = {} + for k, v in six.iteritems(params) if isinstance(params, dict) else params: # noqa: E501 + if k in collection_formats: + collection_format = collection_formats[k] + if collection_format == 'multi': + new_params.extend((k, value) for value in v) + else: + if collection_format == 'ssv': + delimiter = ' ' + elif collection_format == 'tsv': + delimiter = '\t' + elif collection_format == 'pipes': + delimiter = '|' + else: # csv is the default + delimiter = ',' + new_params.append( + (k, delimiter.join(str(value) for value in v))) + else: + new_params.append((k, v)) + return new_params + + def prepare_post_parameters(self, post_params=None, files=None): + """Builds form parameters. + + :param post_params: Normal form parameters. + :param files: File parameters. + :return: Form parameters with files. + """ + params = [] + + if post_params: + params = post_params + + if files: + for k, v in six.iteritems(files): + if not v: + continue + file_names = v if type(v) is list else [v] + for n in file_names: + with open(n, 'rb') as f: + filename = os.path.basename(f.name) + filedata = f.read() + mimetype = (mimetypes.guess_type(filename)[0] or + 'application/octet-stream') + params.append( + tuple([k, tuple([filename, filedata, mimetype])])) + + return params + + def select_header_accept(self, accepts): + """Returns `Accept` based on an array of accepts provided. + + :param accepts: List of headers. + :return: Accept (e.g. application/json). + """ + if not accepts: + return + + accepts = [x.lower() for x in accepts] + + if 'application/json' in accepts: + return 'application/json' + else: + return ', '.join(accepts) + + def select_header_content_type(self, content_types): + """Returns `Content-Type` based on an array of content_types provided. + + :param content_types: List of content-types. + :return: Content-Type (e.g. application/json). + """ + if not content_types: + return 'application/json' + + content_types = [x.lower() for x in content_types] + + if 'application/json' in content_types or '*/*' in content_types: + return 'application/json' + else: + return content_types[0] + + def update_params_for_auth(self, headers, querys, auth_settings): + """Updates header and query params based on authentication setting. + + :param headers: Header parameters dict to be updated. + :param querys: Query parameters tuple list to be updated. + :param auth_settings: Authentication setting identifiers list. + """ + if not auth_settings: + return + + for auth in auth_settings: + auth_setting = self.configuration.auth_settings().get(auth) + if auth_setting: + if not auth_setting['value']: + continue + elif auth_setting['in'] == 'header': + headers[auth_setting['key']] = auth_setting['value'] + elif auth_setting['in'] == 'query': + querys.append((auth_setting['key'], auth_setting['value'])) + else: + raise ValueError( + 'Authentication token must be in `query` or `header`' + ) + + def __deserialize_file(self, response): + """Deserializes body to file + + Saves response body into a file in a temporary folder, + using the filename from the `Content-Disposition` header if provided. + + :param response: RESTResponse. + :return: file path. + """ + fd, path = tempfile.mkstemp(dir=self.configuration.temp_folder_path) + os.close(fd) + os.remove(path) + + content_disposition = response.getheader("Content-Disposition") + if content_disposition: + filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', + content_disposition).group(1) + path = os.path.join(os.path.dirname(path), filename) + + with open(path, "wb") as f: + f.write(response.data) + + return path + + def __deserialize_primitive(self, data, klass): + """Deserializes string to primitive type. + + :param data: str. + :param klass: class literal. + + :return: int, long, float, str, bool. + """ + try: + return klass(data) + except UnicodeEncodeError: + return six.text_type(data) + except TypeError: + return data + + def __deserialize_object(self, value): + """Return a original value. + + :return: object. + """ + return value + + def __deserialize_date(self, string): + """Deserializes string to date. + + :param string: str. + :return: date. + """ + try: + from dateutil.parser import parse + return parse(string).date() + except ImportError: + return string + except ValueError: + raise rest.ApiException( + status=0, + reason="Failed to parse `{0}` as date object".format(string) + ) + + def __deserialize_datatime(self, string): + """Deserializes string to datetime. + + The string should be in iso8601 datetime format. + + :param string: str. + :return: datetime. + """ + try: + from dateutil.parser import parse + return parse(string) + except ImportError: + return string + except ValueError: + raise rest.ApiException( + status=0, + reason=( + "Failed to parse `{0}` as datetime object" + .format(string) + ) + ) + + def __hasattr(self, object, name): + return name in object.__class__.__dict__ + + def __deserialize_model(self, data, klass): + """Deserializes list or dict to model. + + :param data: dict, list. + :param klass: class literal. + :return: model object. + """ + + if (not klass.swagger_types and + not self.__hasattr(klass, 'get_real_child_model')): + return data + + kwargs = {} + if klass.swagger_types is not None: + for attr, attr_type in six.iteritems(klass.swagger_types): + if (data is not None and + klass.attribute_map[attr] in data and + isinstance(data, (list, dict))): + value = data[klass.attribute_map[attr]] + kwargs[attr] = self.__deserialize(value, attr_type) + + instance = klass(**kwargs) + + if (isinstance(instance, dict) and + klass.swagger_types is not None and + isinstance(data, dict)): + for key, value in data.items(): + if key not in klass.swagger_types: + instance[key] = value + if self.__hasattr(instance, 'get_real_child_model'): + klass_name = instance.get_real_child_model(data) + if klass_name: + instance = self.__deserialize(data, klass_name) + return instance diff --git a/sdk/python/kubeflow/pytorchjob/configuration.py b/sdk/python/kubeflow/pytorchjob/configuration.py new file mode 100644 index 000000000..48babe26e --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/configuration.py @@ -0,0 +1,237 @@ +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +import copy +import logging +import multiprocessing +import sys +import urllib3 + +import six +from six.moves import http_client as httplib + + +class Configuration(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Ref: https://github.com/swagger-api/swagger-codegen + Do not edit the class manually. + """ + + _default = None + + def __init__(self): + """Constructor""" + if self._default: + for key in self._default.__dict__.keys(): + self.__dict__[key] = copy.copy(self._default.__dict__[key]) + return + + # Default Base url + self.host = "https://localhost" + # Temp file folder for downloading files + self.temp_folder_path = None + + # Authentication Settings + # dict to store API key(s) + self.api_key = {} + # dict to store API prefix (e.g. Bearer) + self.api_key_prefix = {} + # Username for HTTP basic authentication + self.username = "" + # Password for HTTP basic authentication + self.password = "" + + # Logging Settings + self.logger = {} + self.logger["package_logger"] = logging.getLogger("pytorchjob") + self.logger["urllib3_logger"] = logging.getLogger("urllib3") + # Log format + self.logger_format = '%(asctime)s %(levelname)s %(message)s' + # Log stream handler + self.logger_stream_handler = None + # Log file handler + self.logger_file_handler = None + # Debug file location + self.logger_file = None + # Debug switch + self.debug = False + + # SSL/TLS verification + # Set this to false to skip verifying SSL certificate when calling API + # from https server. + self.verify_ssl = True + # Set this to customize the certificate file to verify the peer. + self.ssl_ca_cert = None + # client certificate file + self.cert_file = None + # client key file + self.key_file = None + # Set this to True/False to enable/disable SSL hostname verification. + self.assert_hostname = None + + # urllib3 connection pool's maximum number of connections saved + # per pool. urllib3 uses 1 connection as default value, but this is + # not the best value when you are making a lot of possibly parallel + # requests to the same host, which is often the case here. + # cpu_count * 5 is used as default value to increase performance. + self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 + + # Proxy URL + self.proxy = None + # Safe chars for path_param + self.safe_chars_for_path_param = '' + + @classmethod + def set_default(cls, default): + cls._default = default + + @property + def logger_file(self): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + return self.__logger_file + + @logger_file.setter + def logger_file(self, value): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + self.__logger_file = value + if self.__logger_file: + # If set logging file, + # then add file handler and remove stream handler. + self.logger_file_handler = logging.FileHandler(self.__logger_file) + self.logger_file_handler.setFormatter(self.logger_formatter) + for _, logger in six.iteritems(self.logger): + logger.addHandler(self.logger_file_handler) + if self.logger_stream_handler: + logger.removeHandler(self.logger_stream_handler) + else: + # If not set logging file, + # then add stream handler and remove file handler. + self.logger_stream_handler = logging.StreamHandler() + self.logger_stream_handler.setFormatter(self.logger_formatter) + for _, logger in six.iteritems(self.logger): + logger.addHandler(self.logger_stream_handler) + if self.logger_file_handler: + logger.removeHandler(self.logger_file_handler) + + @property + def debug(self): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + return self.__debug + + @debug.setter + def debug(self, value): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + self.__debug = value + if self.__debug: + # if debug status is True, turn on debug logging + for _, logger in six.iteritems(self.logger): + logger.setLevel(logging.DEBUG) + # turn on httplib debug + httplib.HTTPConnection.debuglevel = 1 + else: + # if debug status is False, turn off debug logging, + # setting log level to default `logging.WARNING` + for _, logger in six.iteritems(self.logger): + logger.setLevel(logging.WARNING) + # turn off httplib debug + httplib.HTTPConnection.debuglevel = 0 + + @property + def logger_format(self): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + return self.__logger_format + + @logger_format.setter + def logger_format(self, value): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + self.__logger_format = value + self.logger_formatter = logging.Formatter(self.__logger_format) + + def get_api_key_with_prefix(self, identifier): + """Gets API key (with prefix if set). + + :param identifier: The identifier of apiKey. + :return: The token for api key authentication. + """ + if (self.api_key.get(identifier) and + self.api_key_prefix.get(identifier)): + return self.api_key_prefix[identifier] + ' ' + self.api_key[identifier] # noqa: E501 + elif self.api_key.get(identifier): + return self.api_key[identifier] + + def get_basic_auth_token(self): + """Gets HTTP basic authentication header (string). + + :return: The token for basic HTTP authentication. + """ + return urllib3.util.make_headers( + basic_auth=self.username + ':' + self.password + ).get('authorization') + + def auth_settings(self): + """Gets Auth Settings dict for api client. + + :return: The Auth Settings information dict. + """ + return { + + } + + def to_debug_report(self): + """Gets the essential information for debugging. + + :return: The report for debugging. + """ + return "Python SDK Debug Report:\n"\ + "OS: {env}\n"\ + "Python Version: {pyversion}\n"\ + "Version of the API: v0.1\n"\ + "SDK Package Version: 0.1".\ + format(env=sys.platform, pyversion=sys.version) diff --git a/sdk/python/kubeflow/pytorchjob/constants/__init__.py b/sdk/python/kubeflow/pytorchjob/constants/__init__.py new file mode 100644 index 000000000..ede60a09a --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/constants/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/sdk/python/kubeflow/pytorchjob/constants/constants.py b/sdk/python/kubeflow/pytorchjob/constants/constants.py new file mode 100644 index 000000000..8d2b3d8aa --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/constants/constants.py @@ -0,0 +1,23 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +# PyTorchJob K8S constants +PYTORCH_GROUP = "kubeflow.org" +PYTORCH_KIND = "PyTorchJob" +PYTORCH_PLURAL = "pytorchjobs" +PYTORCH_VERSION = "v1" + +PYTORCH_LOGLEVEL = os.environ.get('PyTorchJob_LOGLEVEL', 'INFO').upper() diff --git a/sdk/python/kubeflow/pytorchjob/models/__init__.py b/sdk/python/kubeflow/pytorchjob/models/__init__.py new file mode 100644 index 000000000..03ad9921c --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/models/__init__.py @@ -0,0 +1,38 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +# flake8: noqa +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +# import models into model package +from pytorchjob.models.v1_job_condition import V1JobCondition +from pytorchjob.models.v1_job_status import V1JobStatus +from pytorchjob.models.v1_py_torch_job import V1PyTorchJob +from pytorchjob.models.v1_py_torch_job_list import V1PyTorchJobList +from pytorchjob.models.v1_py_torch_job_spec import V1PyTorchJobSpec +from pytorchjob.models.v1_replica_spec import V1ReplicaSpec +from pytorchjob.models.v1_replica_status import V1ReplicaStatus diff --git a/sdk/python/kubeflow/pytorchjob/models/v1_job_condition.py b/sdk/python/kubeflow/pytorchjob/models/v1_job_condition.py new file mode 100644 index 000000000..84331252a --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/models/v1_job_condition.py @@ -0,0 +1,275 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +import pprint +import re # noqa: F401 + +import six + +from pytorchjob.models.v1_time import V1Time # noqa: F401,E501 + + +class V1JobCondition(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + 'last_transition_time': 'V1Time', + 'last_update_time': 'V1Time', + 'message': 'str', + 'reason': 'str', + 'status': 'str', + 'type': 'str' + } + + attribute_map = { + 'last_transition_time': 'lastTransitionTime', + 'last_update_time': 'lastUpdateTime', + 'message': 'message', + 'reason': 'reason', + 'status': 'status', + 'type': 'type' + } + + def __init__(self, last_transition_time=None, last_update_time=None, message=None, reason=None, status=None, type=None): # noqa: E501 + """V1JobCondition - a model defined in Swagger""" # noqa: E501 + + self._last_transition_time = None + self._last_update_time = None + self._message = None + self._reason = None + self._status = None + self._type = None + self.discriminator = None + + if last_transition_time is not None: + self.last_transition_time = last_transition_time + if last_update_time is not None: + self.last_update_time = last_update_time + if message is not None: + self.message = message + if reason is not None: + self.reason = reason + self.status = status + self.type = type + + @property + def last_transition_time(self): + """Gets the last_transition_time of this V1JobCondition. # noqa: E501 + + Last time the condition transitioned from one status to another. # noqa: E501 + + :return: The last_transition_time of this V1JobCondition. # noqa: E501 + :rtype: V1Time + """ + return self._last_transition_time + + @last_transition_time.setter + def last_transition_time(self, last_transition_time): + """Sets the last_transition_time of this V1JobCondition. + + Last time the condition transitioned from one status to another. # noqa: E501 + + :param last_transition_time: The last_transition_time of this V1JobCondition. # noqa: E501 + :type: V1Time + """ + + self._last_transition_time = last_transition_time + + @property + def last_update_time(self): + """Gets the last_update_time of this V1JobCondition. # noqa: E501 + + The last time this condition was updated. # noqa: E501 + + :return: The last_update_time of this V1JobCondition. # noqa: E501 + :rtype: V1Time + """ + return self._last_update_time + + @last_update_time.setter + def last_update_time(self, last_update_time): + """Sets the last_update_time of this V1JobCondition. + + The last time this condition was updated. # noqa: E501 + + :param last_update_time: The last_update_time of this V1JobCondition. # noqa: E501 + :type: V1Time + """ + + self._last_update_time = last_update_time + + @property + def message(self): + """Gets the message of this V1JobCondition. # noqa: E501 + + A human readable message indicating details about the transition. # noqa: E501 + + :return: The message of this V1JobCondition. # noqa: E501 + :rtype: str + """ + return self._message + + @message.setter + def message(self, message): + """Sets the message of this V1JobCondition. + + A human readable message indicating details about the transition. # noqa: E501 + + :param message: The message of this V1JobCondition. # noqa: E501 + :type: str + """ + + self._message = message + + @property + def reason(self): + """Gets the reason of this V1JobCondition. # noqa: E501 + + The reason for the condition's last transition. # noqa: E501 + + :return: The reason of this V1JobCondition. # noqa: E501 + :rtype: str + """ + return self._reason + + @reason.setter + def reason(self, reason): + """Sets the reason of this V1JobCondition. + + The reason for the condition's last transition. # noqa: E501 + + :param reason: The reason of this V1JobCondition. # noqa: E501 + :type: str + """ + + self._reason = reason + + @property + def status(self): + """Gets the status of this V1JobCondition. # noqa: E501 + + Status of the condition, one of True, False, Unknown. # noqa: E501 + + :return: The status of this V1JobCondition. # noqa: E501 + :rtype: str + """ + return self._status + + @status.setter + def status(self, status): + """Sets the status of this V1JobCondition. + + Status of the condition, one of True, False, Unknown. # noqa: E501 + + :param status: The status of this V1JobCondition. # noqa: E501 + :type: str + """ + if status is None: + raise ValueError("Invalid value for `status`, must not be `None`") # noqa: E501 + + self._status = status + + @property + def type(self): + """Gets the type of this V1JobCondition. # noqa: E501 + + Type of job condition. # noqa: E501 + + :return: The type of this V1JobCondition. # noqa: E501 + :rtype: str + """ + return self._type + + @type.setter + def type(self, type): + """Sets the type of this V1JobCondition. + + Type of job condition. # noqa: E501 + + :param type: The type of this V1JobCondition. # noqa: E501 + :type: str + """ + if type is None: + raise ValueError("Invalid value for `type`, must not be `None`") # noqa: E501 + + self._type = type + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + if issubclass(V1JobCondition, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, V1JobCondition): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/sdk/python/kubeflow/pytorchjob/models/v1_job_status.py b/sdk/python/kubeflow/pytorchjob/models/v1_job_status.py new file mode 100644 index 000000000..f8a2c7ec3 --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/models/v1_job_status.py @@ -0,0 +1,249 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +import pprint +import re # noqa: F401 + +import six + +from kubernetes.client import V1JobCondition # noqa: F401,E501 +from pytorchjob.models.v1_replica_status import V1ReplicaStatus # noqa: F401,E501 +from pytorchjob.models.v1_time import V1Time # noqa: F401,E501 + + +class V1JobStatus(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + 'completion_time': 'V1Time', + 'conditions': 'list[V1JobCondition]', + 'last_reconcile_time': 'V1Time', + 'replica_statuses': 'dict(str, V1ReplicaStatus)', + 'start_time': 'V1Time' + } + + attribute_map = { + 'completion_time': 'completionTime', + 'conditions': 'conditions', + 'last_reconcile_time': 'lastReconcileTime', + 'replica_statuses': 'replicaStatuses', + 'start_time': 'startTime' + } + + def __init__(self, completion_time=None, conditions=None, last_reconcile_time=None, replica_statuses=None, start_time=None): # noqa: E501 + """V1JobStatus - a model defined in Swagger""" # noqa: E501 + + self._completion_time = None + self._conditions = None + self._last_reconcile_time = None + self._replica_statuses = None + self._start_time = None + self.discriminator = None + + if completion_time is not None: + self.completion_time = completion_time + self.conditions = conditions + if last_reconcile_time is not None: + self.last_reconcile_time = last_reconcile_time + self.replica_statuses = replica_statuses + if start_time is not None: + self.start_time = start_time + + @property + def completion_time(self): + """Gets the completion_time of this V1JobStatus. # noqa: E501 + + Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. # noqa: E501 + + :return: The completion_time of this V1JobStatus. # noqa: E501 + :rtype: V1Time + """ + return self._completion_time + + @completion_time.setter + def completion_time(self, completion_time): + """Sets the completion_time of this V1JobStatus. + + Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. # noqa: E501 + + :param completion_time: The completion_time of this V1JobStatus. # noqa: E501 + :type: V1Time + """ + + self._completion_time = completion_time + + @property + def conditions(self): + """Gets the conditions of this V1JobStatus. # noqa: E501 + + Conditions is an array of current observed job conditions. # noqa: E501 + + :return: The conditions of this V1JobStatus. # noqa: E501 + :rtype: list[V1JobCondition] + """ + return self._conditions + + @conditions.setter + def conditions(self, conditions): + """Sets the conditions of this V1JobStatus. + + Conditions is an array of current observed job conditions. # noqa: E501 + + :param conditions: The conditions of this V1JobStatus. # noqa: E501 + :type: list[V1JobCondition] + """ + if conditions is None: + raise ValueError("Invalid value for `conditions`, must not be `None`") # noqa: E501 + + self._conditions = conditions + + @property + def last_reconcile_time(self): + """Gets the last_reconcile_time of this V1JobStatus. # noqa: E501 + + Represents last time when the job was reconciled. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. # noqa: E501 + + :return: The last_reconcile_time of this V1JobStatus. # noqa: E501 + :rtype: V1Time + """ + return self._last_reconcile_time + + @last_reconcile_time.setter + def last_reconcile_time(self, last_reconcile_time): + """Sets the last_reconcile_time of this V1JobStatus. + + Represents last time when the job was reconciled. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. # noqa: E501 + + :param last_reconcile_time: The last_reconcile_time of this V1JobStatus. # noqa: E501 + :type: V1Time + """ + + self._last_reconcile_time = last_reconcile_time + + @property + def replica_statuses(self): + """Gets the replica_statuses of this V1JobStatus. # noqa: E501 + + ReplicaStatuses is map of ReplicaType and ReplicaStatus, specifies the status of each replica. # noqa: E501 + + :return: The replica_statuses of this V1JobStatus. # noqa: E501 + :rtype: dict(str, V1ReplicaStatus) + """ + return self._replica_statuses + + @replica_statuses.setter + def replica_statuses(self, replica_statuses): + """Sets the replica_statuses of this V1JobStatus. + + ReplicaStatuses is map of ReplicaType and ReplicaStatus, specifies the status of each replica. # noqa: E501 + + :param replica_statuses: The replica_statuses of this V1JobStatus. # noqa: E501 + :type: dict(str, V1ReplicaStatus) + """ + if replica_statuses is None: + raise ValueError("Invalid value for `replica_statuses`, must not be `None`") # noqa: E501 + + self._replica_statuses = replica_statuses + + @property + def start_time(self): + """Gets the start_time of this V1JobStatus. # noqa: E501 + + Represents time when the job was acknowledged by the job controller. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. # noqa: E501 + + :return: The start_time of this V1JobStatus. # noqa: E501 + :rtype: V1Time + """ + return self._start_time + + @start_time.setter + def start_time(self, start_time): + """Sets the start_time of this V1JobStatus. + + Represents time when the job was acknowledged by the job controller. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. # noqa: E501 + + :param start_time: The start_time of this V1JobStatus. # noqa: E501 + :type: V1Time + """ + + self._start_time = start_time + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + if issubclass(V1JobStatus, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, V1JobStatus): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/sdk/python/kubeflow/pytorchjob/models/v1_py_torch_job.py b/sdk/python/kubeflow/pytorchjob/models/v1_py_torch_job.py new file mode 100644 index 000000000..d6cecc004 --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/models/v1_py_torch_job.py @@ -0,0 +1,247 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +import pprint +import re # noqa: F401 + +import six + +from kubernetes.client import V1ObjectMeta # noqa: F401,E501 +from pytorchjob.models.v1_job_status import V1JobStatus # noqa: F401,E501 +from pytorchjob.models.v1_py_torch_job_spec import V1PyTorchJobSpec # noqa: F401,E501 + + +class V1PyTorchJob(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + 'api_version': 'str', + 'kind': 'str', + 'metadata': 'V1ObjectMeta', + 'spec': 'V1PyTorchJobSpec', + 'status': 'V1JobStatus' + } + + attribute_map = { + 'api_version': 'apiVersion', + 'kind': 'kind', + 'metadata': 'metadata', + 'spec': 'spec', + 'status': 'status' + } + + def __init__(self, api_version=None, kind=None, metadata=None, spec=None, status=None): # noqa: E501 + """V1PyTorchJob - a model defined in Swagger""" # noqa: E501 + + self._api_version = None + self._kind = None + self._metadata = None + self._spec = None + self._status = None + self.discriminator = None + + if api_version is not None: + self.api_version = api_version + if kind is not None: + self.kind = kind + if metadata is not None: + self.metadata = metadata + if spec is not None: + self.spec = spec + if status is not None: + self.status = status + + @property + def api_version(self): + """Gets the api_version of this V1PyTorchJob. # noqa: E501 + + APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources # noqa: E501 + + :return: The api_version of this V1PyTorchJob. # noqa: E501 + :rtype: str + """ + return self._api_version + + @api_version.setter + def api_version(self, api_version): + """Sets the api_version of this V1PyTorchJob. + + APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources # noqa: E501 + + :param api_version: The api_version of this V1PyTorchJob. # noqa: E501 + :type: str + """ + + self._api_version = api_version + + @property + def kind(self): + """Gets the kind of this V1PyTorchJob. # noqa: E501 + + Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds # noqa: E501 + + :return: The kind of this V1PyTorchJob. # noqa: E501 + :rtype: str + """ + return self._kind + + @kind.setter + def kind(self, kind): + """Sets the kind of this V1PyTorchJob. + + Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds # noqa: E501 + + :param kind: The kind of this V1PyTorchJob. # noqa: E501 + :type: str + """ + + self._kind = kind + + @property + def metadata(self): + """Gets the metadata of this V1PyTorchJob. # noqa: E501 + + Standard Kubernetes object's metadata. # noqa: E501 + + :return: The metadata of this V1PyTorchJob. # noqa: E501 + :rtype: V1ObjectMeta + """ + return self._metadata + + @metadata.setter + def metadata(self, metadata): + """Sets the metadata of this V1PyTorchJob. + + Standard Kubernetes object's metadata. # noqa: E501 + + :param metadata: The metadata of this V1PyTorchJob. # noqa: E501 + :type: V1ObjectMeta + """ + + self._metadata = metadata + + @property + def spec(self): + """Gets the spec of this V1PyTorchJob. # noqa: E501 + + Specification of the desired state of the PyTorchJob. # noqa: E501 + + :return: The spec of this V1PyTorchJob. # noqa: E501 + :rtype: V1PyTorchJobSpec + """ + return self._spec + + @spec.setter + def spec(self, spec): + """Sets the spec of this V1PyTorchJob. + + Specification of the desired state of the PyTorchJob. # noqa: E501 + + :param spec: The spec of this V1PyTorchJob. # noqa: E501 + :type: V1PyTorchJobSpec + """ + + self._spec = spec + + @property + def status(self): + """Gets the status of this V1PyTorchJob. # noqa: E501 + + Most recently observed status of the PyTorchJob. Read-only (modified by the system). # noqa: E501 + + :return: The status of this V1PyTorchJob. # noqa: E501 + :rtype: V1JobStatus + """ + return self._status + + @status.setter + def status(self, status): + """Sets the status of this V1PyTorchJob. + + Most recently observed status of the PyTorchJob. Read-only (modified by the system). # noqa: E501 + + :param status: The status of this V1PyTorchJob. # noqa: E501 + :type: V1JobStatus + """ + + self._status = status + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + if issubclass(V1PyTorchJob, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, V1PyTorchJob): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/sdk/python/kubeflow/pytorchjob/models/v1_py_torch_job_list.py b/sdk/python/kubeflow/pytorchjob/models/v1_py_torch_job_list.py new file mode 100644 index 000000000..340de3f3f --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/models/v1_py_torch_job_list.py @@ -0,0 +1,220 @@ + +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +import pprint +import re # noqa: F401 + +import six + +from kubernetes.client import V1ListMeta # noqa: F401,E501 +from pytorchjob.models.v1_py_torch_job import V1PyTorchJob # noqa: F401,E501 + + +class V1PyTorchJobList(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + 'api_version': 'str', + 'items': 'list[V1PyTorchJob]', + 'kind': 'str', + 'metadata': 'V1ListMeta' + } + + attribute_map = { + 'api_version': 'apiVersion', + 'items': 'items', + 'kind': 'kind', + 'metadata': 'metadata' + } + + def __init__(self, api_version=None, items=None, kind=None, metadata=None): # noqa: E501 + """V1PyTorchJobList - a model defined in Swagger""" # noqa: E501 + + self._api_version = None + self._items = None + self._kind = None + self._metadata = None + self.discriminator = None + + if api_version is not None: + self.api_version = api_version + self.items = items + if kind is not None: + self.kind = kind + if metadata is not None: + self.metadata = metadata + + @property + def api_version(self): + """Gets the api_version of this V1PyTorchJobList. # noqa: E501 + + APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources # noqa: E501 + + :return: The api_version of this V1PyTorchJobList. # noqa: E501 + :rtype: str + """ + return self._api_version + + @api_version.setter + def api_version(self, api_version): + """Sets the api_version of this V1PyTorchJobList. + + APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources # noqa: E501 + + :param api_version: The api_version of this V1PyTorchJobList. # noqa: E501 + :type: str + """ + + self._api_version = api_version + + @property + def items(self): + """Gets the items of this V1PyTorchJobList. # noqa: E501 + + List of PyTorchJobs. # noqa: E501 + + :return: The items of this V1PyTorchJobList. # noqa: E501 + :rtype: list[V1PyTorchJob] + """ + return self._items + + @items.setter + def items(self, items): + """Sets the items of this V1PyTorchJobList. + + List of PyTorchJobs. # noqa: E501 + + :param items: The items of this V1PyTorchJobList. # noqa: E501 + :type: list[V1PyTorchJob] + """ + if items is None: + raise ValueError("Invalid value for `items`, must not be `None`") # noqa: E501 + + self._items = items + + @property + def kind(self): + """Gets the kind of this V1PyTorchJobList. # noqa: E501 + + Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds # noqa: E501 + + :return: The kind of this V1PyTorchJobList. # noqa: E501 + :rtype: str + """ + return self._kind + + @kind.setter + def kind(self, kind): + """Sets the kind of this V1PyTorchJobList. + + Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds # noqa: E501 + + :param kind: The kind of this V1PyTorchJobList. # noqa: E501 + :type: str + """ + + self._kind = kind + + @property + def metadata(self): + """Gets the metadata of this V1PyTorchJobList. # noqa: E501 + + Standard list metadata. # noqa: E501 + + :return: The metadata of this V1PyTorchJobList. # noqa: E501 + :rtype: V1ListMeta + """ + return self._metadata + + @metadata.setter + def metadata(self, metadata): + """Sets the metadata of this V1PyTorchJobList. + + Standard list metadata. # noqa: E501 + + :param metadata: The metadata of this V1PyTorchJobList. # noqa: E501 + :type: V1ListMeta + """ + + self._metadata = metadata + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + if issubclass(V1PyTorchJobList, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, V1PyTorchJobList): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/sdk/python/kubeflow/pytorchjob/models/v1_py_torch_job_spec.py b/sdk/python/kubeflow/pytorchjob/models/v1_py_torch_job_spec.py new file mode 100644 index 000000000..a191cbcd1 --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/models/v1_py_torch_job_spec.py @@ -0,0 +1,246 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +import pprint +import re # noqa: F401 + +import six + +from pytorchjob.models.v1_replica_spec import V1ReplicaSpec # noqa: F401,E501 + + +class V1PyTorchJobSpec(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + 'active_deadline_seconds': 'int', + 'backoff_limit': 'int', + 'clean_pod_policy': 'str', + 'pytorch_replica_specs': 'dict(str, V1ReplicaSpec)', + 'ttl_seconds_after_finished': 'int' + } + + attribute_map = { + 'active_deadline_seconds': 'activeDeadlineSeconds', + 'backoff_limit': 'backoffLimit', + 'clean_pod_policy': 'cleanPodPolicy', + 'pytorch_replica_specs': 'pytorchReplicaSpecs', + 'ttl_seconds_after_finished': 'ttlSecondsAfterFinished' + } + + def __init__(self, active_deadline_seconds=None, backoff_limit=None, clean_pod_policy=None, pytorch_replica_specs=None, ttl_seconds_after_finished=None): # noqa: E501 + """V1PyTorchJobSpec - a model defined in Swagger""" # noqa: E501 + + self._active_deadline_seconds = None + self._backoff_limit = None + self._clean_pod_policy = None + self._pytorch_replica_specs = None + self._ttl_seconds_after_finished = None + self.discriminator = None + + if active_deadline_seconds is not None: + self.active_deadline_seconds = active_deadline_seconds + if backoff_limit is not None: + self.backoff_limit = backoff_limit + if clean_pod_policy is not None: + self.clean_pod_policy = clean_pod_policy + self.pytorch_replica_specs = pytorch_replica_specs + if ttl_seconds_after_finished is not None: + self.ttl_seconds_after_finished = ttl_seconds_after_finished + + @property + def active_deadline_seconds(self): + """Gets the active_deadline_seconds of this V1PyTorchJobSpec. # noqa: E501 + + Specifies the duration (in seconds) since startTime during which the job can remain active before it is terminated. Must be a positive integer. This setting applies only to pods where restartPolicy is OnFailure or Always. # noqa: E501 + + :return: The active_deadline_seconds of this V1PyTorchJobSpec. # noqa: E501 + :rtype: int + """ + return self._active_deadline_seconds + + @active_deadline_seconds.setter + def active_deadline_seconds(self, active_deadline_seconds): + """Sets the active_deadline_seconds of this V1PyTorchJobSpec. + + Specifies the duration (in seconds) since startTime during which the job can remain active before it is terminated. Must be a positive integer. This setting applies only to pods where restartPolicy is OnFailure or Always. # noqa: E501 + + :param active_deadline_seconds: The active_deadline_seconds of this V1PyTorchJobSpec. # noqa: E501 + :type: int + """ + + self._active_deadline_seconds = active_deadline_seconds + + @property + def backoff_limit(self): + """Gets the backoff_limit of this V1PyTorchJobSpec. # noqa: E501 + + Number of retries before marking this job as failed. # noqa: E501 + + :return: The backoff_limit of this V1PyTorchJobSpec. # noqa: E501 + :rtype: int + """ + return self._backoff_limit + + @backoff_limit.setter + def backoff_limit(self, backoff_limit): + """Sets the backoff_limit of this V1PyTorchJobSpec. + + Number of retries before marking this job as failed. # noqa: E501 + + :param backoff_limit: The backoff_limit of this V1PyTorchJobSpec. # noqa: E501 + :type: int + """ + + self._backoff_limit = backoff_limit + + @property + def clean_pod_policy(self): + """Gets the clean_pod_policy of this V1PyTorchJobSpec. # noqa: E501 + + Defines the policy for cleaning up pods after the PyTorchJob completes. Defaults to None. # noqa: E501 + + :return: The clean_pod_policy of this V1PyTorchJobSpec. # noqa: E501 + :rtype: str + """ + return self._clean_pod_policy + + @clean_pod_policy.setter + def clean_pod_policy(self, clean_pod_policy): + """Sets the clean_pod_policy of this V1PyTorchJobSpec. + + Defines the policy for cleaning up pods after the PyTorchJob completes. Defaults to None. # noqa: E501 + + :param clean_pod_policy: The clean_pod_policy of this V1PyTorchJobSpec. # noqa: E501 + :type: str + """ + + self._clean_pod_policy = clean_pod_policy + + @property + def pytorch_replica_specs(self): + """Gets the pytorch_replica_specs of this V1PyTorchJobSpec. # noqa: E501 + + A map of PyTorchReplicaType (type) to ReplicaSpec (value). Specifies the PyTorch cluster configuration. For example, { \"Master\": PyTorchReplicaSpec, \"Worker\": PyTorchReplicaSpec, } # noqa: E501 + + :return: The pytorch_replica_specs of this V1PyTorchJobSpec. # noqa: E501 + :rtype: dict(str, V1ReplicaSpec) + """ + return self._pytorch_replica_specs + + @pytorch_replica_specs.setter + def pytorch_replica_specs(self, pytorch_replica_specs): + """Sets the pytorch_replica_specs of this V1PyTorchJobSpec. + + A map of PyTorchReplicaType (type) to ReplicaSpec (value). Specifies the PyTorch cluster configuration. For example, { \"Master\": PyTorchReplicaSpec, \"Worker\": PyTorchReplicaSpec, } # noqa: E501 + + :param pytorch_replica_specs: The pytorch_replica_specs of this V1PyTorchJobSpec. # noqa: E501 + :type: dict(str, V1ReplicaSpec) + """ + if pytorch_replica_specs is None: + raise ValueError("Invalid value for `pytorch_replica_specs`, must not be `None`") # noqa: E501 + + self._pytorch_replica_specs = pytorch_replica_specs + + @property + def ttl_seconds_after_finished(self): + """Gets the ttl_seconds_after_finished of this V1PyTorchJobSpec. # noqa: E501 + + Defines the TTL for cleaning up finished PyTorchJobs (temporary before Kubernetes adds the cleanup controller). It may take extra ReconcilePeriod seconds for the cleanup, since reconcile gets called periodically. Defaults to infinite. # noqa: E501 + + :return: The ttl_seconds_after_finished of this V1PyTorchJobSpec. # noqa: E501 + :rtype: int + """ + return self._ttl_seconds_after_finished + + @ttl_seconds_after_finished.setter + def ttl_seconds_after_finished(self, ttl_seconds_after_finished): + """Sets the ttl_seconds_after_finished of this V1PyTorchJobSpec. + + Defines the TTL for cleaning up finished PyTorchJobs (temporary before Kubernetes adds the cleanup controller). It may take extra ReconcilePeriod seconds for the cleanup, since reconcile gets called periodically. Defaults to infinite. # noqa: E501 + + :param ttl_seconds_after_finished: The ttl_seconds_after_finished of this V1PyTorchJobSpec. # noqa: E501 + :type: int + """ + + self._ttl_seconds_after_finished = ttl_seconds_after_finished + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + if issubclass(V1PyTorchJobSpec, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, V1PyTorchJobSpec): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/sdk/python/kubeflow/pytorchjob/models/v1_replica_spec.py b/sdk/python/kubeflow/pytorchjob/models/v1_replica_spec.py new file mode 100644 index 000000000..8bca62b2e --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/models/v1_replica_spec.py @@ -0,0 +1,189 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +import pprint +import re # noqa: F401 + +import six + +from kubernetes.client import V1PodTemplateSpec # noqa: F401,E501 + + +class V1ReplicaSpec(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + 'replicas': 'int', + 'restart_policy': 'str', + 'template': 'V1PodTemplateSpec' + } + + attribute_map = { + 'replicas': 'replicas', + 'restart_policy': 'restartPolicy', + 'template': 'template' + } + + def __init__(self, replicas=None, restart_policy=None, template=None): # noqa: E501 + """V1ReplicaSpec - a model defined in Swagger""" # noqa: E501 + + self._replicas = None + self._restart_policy = None + self._template = None + self.discriminator = None + + if replicas is not None: + self.replicas = replicas + if restart_policy is not None: + self.restart_policy = restart_policy + if template is not None: + self.template = template + + @property + def replicas(self): + """Gets the replicas of this V1ReplicaSpec. # noqa: E501 + + Replicas is the desired number of replicas of the given template. If unspecified, defaults to 1. # noqa: E501 + + :return: The replicas of this V1ReplicaSpec. # noqa: E501 + :rtype: int + """ + return self._replicas + + @replicas.setter + def replicas(self, replicas): + """Sets the replicas of this V1ReplicaSpec. + + Replicas is the desired number of replicas of the given template. If unspecified, defaults to 1. # noqa: E501 + + :param replicas: The replicas of this V1ReplicaSpec. # noqa: E501 + :type: int + """ + + self._replicas = replicas + + @property + def restart_policy(self): + """Gets the restart_policy of this V1ReplicaSpec. # noqa: E501 + + Restart policy for all replicas within the job. One of Always, OnFailure, Never and ExitCode. Default to Never. # noqa: E501 + + :return: The restart_policy of this V1ReplicaSpec. # noqa: E501 + :rtype: str + """ + return self._restart_policy + + @restart_policy.setter + def restart_policy(self, restart_policy): + """Sets the restart_policy of this V1ReplicaSpec. + + Restart policy for all replicas within the job. One of Always, OnFailure, Never and ExitCode. Default to Never. # noqa: E501 + + :param restart_policy: The restart_policy of this V1ReplicaSpec. # noqa: E501 + :type: str + """ + + self._restart_policy = restart_policy + + @property + def template(self): + """Gets the template of this V1ReplicaSpec. # noqa: E501 + + Template is the object that describes the pod that will be created for this replica. RestartPolicy in PodTemplateSpec will be overide by RestartPolicy in ReplicaSpec # noqa: E501 + + :return: The template of this V1ReplicaSpec. # noqa: E501 + :rtype: V1PodTemplateSpec + """ + return self._template + + @template.setter + def template(self, template): + """Sets the template of this V1ReplicaSpec. + + Template is the object that describes the pod that will be created for this replica. RestartPolicy in PodTemplateSpec will be overide by RestartPolicy in ReplicaSpec # noqa: E501 + + :param template: The template of this V1ReplicaSpec. # noqa: E501 + :type: V1PodTemplateSpec + """ + + self._template = template + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + if issubclass(V1ReplicaSpec, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, V1ReplicaSpec): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/sdk/python/kubeflow/pytorchjob/models/v1_replica_status.py b/sdk/python/kubeflow/pytorchjob/models/v1_replica_status.py new file mode 100644 index 000000000..dd7a40ea7 --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/models/v1_replica_status.py @@ -0,0 +1,187 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +import pprint +import re # noqa: F401 + +import six + + +class V1ReplicaStatus(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + 'active': 'int', + 'failed': 'int', + 'succeeded': 'int' + } + + attribute_map = { + 'active': 'active', + 'failed': 'failed', + 'succeeded': 'succeeded' + } + + def __init__(self, active=None, failed=None, succeeded=None): # noqa: E501 + """V1ReplicaStatus - a model defined in Swagger""" # noqa: E501 + + self._active = None + self._failed = None + self._succeeded = None + self.discriminator = None + + if active is not None: + self.active = active + if failed is not None: + self.failed = failed + if succeeded is not None: + self.succeeded = succeeded + + @property + def active(self): + """Gets the active of this V1ReplicaStatus. # noqa: E501 + + The number of actively running pods. # noqa: E501 + + :return: The active of this V1ReplicaStatus. # noqa: E501 + :rtype: int + """ + return self._active + + @active.setter + def active(self, active): + """Sets the active of this V1ReplicaStatus. + + The number of actively running pods. # noqa: E501 + + :param active: The active of this V1ReplicaStatus. # noqa: E501 + :type: int + """ + + self._active = active + + @property + def failed(self): + """Gets the failed of this V1ReplicaStatus. # noqa: E501 + + The number of pods which reached phase Failed. # noqa: E501 + + :return: The failed of this V1ReplicaStatus. # noqa: E501 + :rtype: int + """ + return self._failed + + @failed.setter + def failed(self, failed): + """Sets the failed of this V1ReplicaStatus. + + The number of pods which reached phase Failed. # noqa: E501 + + :param failed: The failed of this V1ReplicaStatus. # noqa: E501 + :type: int + """ + + self._failed = failed + + @property + def succeeded(self): + """Gets the succeeded of this V1ReplicaStatus. # noqa: E501 + + The number of pods which reached phase Succeeded. # noqa: E501 + + :return: The succeeded of this V1ReplicaStatus. # noqa: E501 + :rtype: int + """ + return self._succeeded + + @succeeded.setter + def succeeded(self, succeeded): + """Sets the succeeded of this V1ReplicaStatus. + + The number of pods which reached phase Succeeded. # noqa: E501 + + :param succeeded: The succeeded of this V1ReplicaStatus. # noqa: E501 + :type: int + """ + + self._succeeded = succeeded + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + if issubclass(V1ReplicaStatus, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, V1ReplicaStatus): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/sdk/python/kubeflow/pytorchjob/models/v1_time.py b/sdk/python/kubeflow/pytorchjob/models/v1_time.py new file mode 100644 index 000000000..f20226eec --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/models/v1_time.py @@ -0,0 +1,101 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorchjob + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +import pprint +import re # noqa: F401 + +import six + + +class V1Time(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + } + + attribute_map = { + } + + def __init__(self): # noqa: E501 + """V1Time - a model defined in Swagger""" # noqa: E501 + self.discriminator = None + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + if issubclass(V1Time, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, V1Time): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/sdk/python/kubeflow/pytorchjob/rest.py b/sdk/python/kubeflow/pytorchjob/rest.py new file mode 100644 index 000000000..02686c69d --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/rest.py @@ -0,0 +1,323 @@ +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +import io +import json +import logging +import re +import ssl + +import certifi +# python 2 and python 3 compatibility library +import six +from six.moves.urllib.parse import urlencode + +try: + import urllib3 +except ImportError: + raise ImportError('Swagger python client requires urllib3.') + + +logger = logging.getLogger(__name__) + + +class RESTResponse(io.IOBase): + + def __init__(self, resp): + self.urllib3_response = resp + self.status = resp.status + self.reason = resp.reason + self.data = resp.data + + def getheaders(self): + """Returns a dictionary of the response headers.""" + return self.urllib3_response.getheaders() + + def getheader(self, name, default=None): + """Returns a given response header.""" + return self.urllib3_response.getheader(name, default) + + +class RESTClientObject(object): + + def __init__(self, configuration, pools_size=4, maxsize=None): + # urllib3.PoolManager will pass all kw parameters to connectionpool + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 + # maxsize is the number of requests to host that are allowed in parallel # noqa: E501 + # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html # noqa: E501 + + # cert_reqs + if configuration.verify_ssl: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + + # ca_certs + if configuration.ssl_ca_cert: + ca_certs = configuration.ssl_ca_cert + else: + # if not set certificate file, use Mozilla's root certificates. + ca_certs = certifi.where() + + addition_pool_args = {} + if configuration.assert_hostname is not None: + addition_pool_args['assert_hostname'] = configuration.assert_hostname # noqa: E501 + + if maxsize is None: + if configuration.connection_pool_maxsize is not None: + maxsize = configuration.connection_pool_maxsize + else: + maxsize = 4 + + # https pool manager + if configuration.proxy: + self.pool_manager = urllib3.ProxyManager( + num_pools=pools_size, + maxsize=maxsize, + cert_reqs=cert_reqs, + ca_certs=ca_certs, + cert_file=configuration.cert_file, + key_file=configuration.key_file, + proxy_url=configuration.proxy, + **addition_pool_args + ) + else: + self.pool_manager = urllib3.PoolManager( + num_pools=pools_size, + maxsize=maxsize, + cert_reqs=cert_reqs, + ca_certs=ca_certs, + cert_file=configuration.cert_file, + key_file=configuration.key_file, + **addition_pool_args + ) + + def request(self, method, url, query_params=None, headers=None, + body=None, post_params=None, _preload_content=True, + _request_timeout=None): + """Perform requests. + + :param method: http request method + :param url: http request url + :param query_params: query parameters in the url + :param headers: http request headers + :param body: request json body, for `application/json` + :param post_params: request post parameters, + `application/x-www-form-urlencoded` + and `multipart/form-data` + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + """ + method = method.upper() + assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT', + 'PATCH', 'OPTIONS'] + + if post_params and body: + raise ValueError( + "body parameter cannot be used with post_params parameter." + ) + + post_params = post_params or {} + headers = headers or {} + + timeout = None + if _request_timeout: + if isinstance(_request_timeout, (int, ) if six.PY3 else (int, long)): # noqa: E501,F821 + timeout = urllib3.Timeout(total=_request_timeout) + elif (isinstance(_request_timeout, tuple) and + len(_request_timeout) == 2): + timeout = urllib3.Timeout( + connect=_request_timeout[0], read=_request_timeout[1]) + + if 'Content-Type' not in headers: + headers['Content-Type'] = 'application/json' + + try: + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` + if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: + if query_params: + url += '?' + urlencode(query_params) + if re.search('json', headers['Content-Type'], re.IGNORECASE): + request_body = None + if body is not None: + request_body = json.dumps(body) + r = self.pool_manager.request( + method, url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 + r = self.pool_manager.request( + method, url, + fields=post_params, + encode_multipart=False, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + elif headers['Content-Type'] == 'multipart/form-data': + # must del headers['Content-Type'], or the correct + # Content-Type which generated by urllib3 will be + # overwritten. + del headers['Content-Type'] + r = self.pool_manager.request( + method, url, + fields=post_params, + encode_multipart=True, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + # Pass a `string` parameter directly in the body to support + # other content types than Json when `body` argument is + # provided in serialized form + elif isinstance(body, str): + request_body = body + r = self.pool_manager.request( + method, url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + else: + # Cannot generate the request from given parameters + msg = """Cannot prepare a request message for provided + arguments. Please check that your arguments match + declared content type.""" + raise ApiException(status=0, reason=msg) + # For `GET`, `HEAD` + else: + r = self.pool_manager.request(method, url, + fields=query_params, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + except urllib3.exceptions.SSLError as e: + msg = "{0}\n{1}".format(type(e).__name__, str(e)) + raise ApiException(status=0, reason=msg) + + if _preload_content: + r = RESTResponse(r) + + # In the python 3, the response.data is bytes. + # we need to decode it to string. + if six.PY3: + r.data = r.data.decode('utf8') + + # log response body + logger.debug("response body: %s", r.data) + + if not 200 <= r.status <= 299: + raise ApiException(http_resp=r) + + return r + + def GET(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): + return self.request("GET", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params) + + def HEAD(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): + return self.request("HEAD", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params) + + def OPTIONS(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("OPTIONS", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def DELETE(self, url, headers=None, query_params=None, body=None, + _preload_content=True, _request_timeout=None): + return self.request("DELETE", url, + headers=headers, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def POST(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("POST", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def PUT(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("PUT", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def PATCH(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("PATCH", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + +class ApiException(Exception): + + def __init__(self, status=None, reason=None, http_resp=None): + if http_resp: + self.status = http_resp.status + self.reason = http_resp.reason + self.body = http_resp.data + self.headers = http_resp.getheaders() + else: + self.status = status + self.reason = reason + self.body = None + self.headers = None + + def __str__(self): + """Custom error messages for exception""" + error_message = "({0})\n"\ + "Reason: {1}\n".format(self.status, self.reason) + if self.headers: + error_message += "HTTP response headers: {0}\n".format( + self.headers) + + if self.body: + error_message += "HTTP response body: {0}\n".format(self.body) + + return error_message diff --git a/sdk/python/kubeflow/pytorchjob/utils/__init__.py b/sdk/python/kubeflow/pytorchjob/utils/__init__.py new file mode 100644 index 000000000..ede60a09a --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/utils/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/sdk/python/kubeflow/pytorchjob/utils/utils.py b/sdk/python/kubeflow/pytorchjob/utils/utils.py new file mode 100644 index 000000000..7b5c9b53a --- /dev/null +++ b/sdk/python/kubeflow/pytorchjob/utils/utils.py @@ -0,0 +1,35 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +def is_running_in_k8s(): + return os.path.isdir('/var/run/secrets/kubernetes.io/') + + +def get_current_k8s_namespace(): + with open('/var/run/secrets/kubernetes.io/serviceaccount/namespace', 'r') as f: + return f.readline() + + +def get_default_target_namespace(): + if not is_running_in_k8s(): + return 'default' + return get_current_k8s_namespace() + + +def set_pytorchjob_namespace(pytorchjob): + pytorchjob_namespace = pytorchjob.metadata.namespace + namespace = pytorchjob_namespace or get_default_target_namespace() + return namespace diff --git a/sdk/python/requirements.txt b/sdk/python/requirements.txt new file mode 100644 index 000000000..7636cc26b --- /dev/null +++ b/sdk/python/requirements.txt @@ -0,0 +1,6 @@ +certifi >= 14.05.14 +six >= 1.10 +python_dateutil >= 2.5.3 +setuptools >= 21.0.0 +urllib3 >= 1.15.1 +kubernetes>=10.0.1 diff --git a/sdk/python/setup.py b/sdk/python/setup.py new file mode 100644 index 000000000..d413925a9 --- /dev/null +++ b/sdk/python/setup.py @@ -0,0 +1,60 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import setuptools + +TESTS_REQUIRES = [ + 'pytest', + 'pytest-tornasync', + 'mypy' +] + +with open('requirements.txt') as f: + REQUIRES = f.readlines() + +setuptools.setup( + name='kubeflow-pytorchjob', + version='0.0.1', + author="Kubeflow Authors", + author_email='hejinchi@cn.ibm.com', + license="Apache License Version 2.0", + url="https://github.com/kubeflow/pytorch-operator/sdk/python", + description="PyTorchJob Python SDK", + long_description="PyTorchJob Python SDK", + packages=setuptools.find_packages( + include=("kubeflow*")), + package_data={}, + include_package_data=False, + zip_safe=False, + classifiers=[ + 'Intended Audience :: Developers', + 'Intended Audience :: Education', + 'Intended Audience :: Science/Research', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + 'Topic :: Scientific/Engineering', + 'Topic :: Scientific/Engineering :: Artificial Intelligence', + 'Topic :: Software Development', + 'Topic :: Software Development :: Libraries', + 'Topic :: Software Development :: Libraries :: Python Modules', + ], + install_requires=REQUIRES, + tests_require=TESTS_REQUIRES, + extras_require={'test': TESTS_REQUIRES} +) diff --git a/sdk/python/test/__init__.py b/sdk/python/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sdk/python/test/test_v1_job_condition.py b/sdk/python/test/test_v1_job_condition.py new file mode 100644 index 000000000..591f9bdce --- /dev/null +++ b/sdk/python/test/test_v1_job_condition.py @@ -0,0 +1,54 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +import unittest + +import pytorchjob +from pytorchjob.models.v1_job_condition import V1JobCondition # noqa: E501 +from pytorchjob.rest import ApiException + + +class TestV1JobCondition(unittest.TestCase): + """V1JobCondition unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testV1JobCondition(self): + """Test V1JobCondition""" + # FIXME: construct object with mandatory attributes with example values + # model = pytorchjob.models.v1_job_condition.V1JobCondition() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/sdk/python/test/test_v1_job_status.py b/sdk/python/test/test_v1_job_status.py new file mode 100644 index 000000000..3f69cbeb7 --- /dev/null +++ b/sdk/python/test/test_v1_job_status.py @@ -0,0 +1,54 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +import unittest + +import pytorchjob +from pytorchjob.models.v1_job_status import V1JobStatus # noqa: E501 +from pytorchjob.rest import ApiException + + +class TestV1JobStatus(unittest.TestCase): + """V1JobStatus unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testV1JobStatus(self): + """Test V1JobStatus""" + # FIXME: construct object with mandatory attributes with example values + # model = pytorchjob.models.v1_job_status.V1JobStatus() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/sdk/python/test/test_v1_py_torch_job.py b/sdk/python/test/test_v1_py_torch_job.py new file mode 100644 index 000000000..9174940c2 --- /dev/null +++ b/sdk/python/test/test_v1_py_torch_job.py @@ -0,0 +1,54 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +import unittest + +import pytorchjob +from pytorchjob.models.v1_py_torch_job import V1PyTorchJob # noqa: E501 +from pytorchjob.rest import ApiException + + +class TestV1PyTorchJob(unittest.TestCase): + """V1PyTorchJob unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testV1PyTorchJob(self): + """Test V1PyTorchJob""" + # FIXME: construct object with mandatory attributes with example values + # model = pytorchjob.models.v1_py_torch_job.V1PyTorchJob() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/sdk/python/test/test_v1_py_torch_job_list.py b/sdk/python/test/test_v1_py_torch_job_list.py new file mode 100644 index 000000000..45bde3b75 --- /dev/null +++ b/sdk/python/test/test_v1_py_torch_job_list.py @@ -0,0 +1,54 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +import unittest + +import pytorchjob +from pytorchjob.models.v1_py_torch_job_list import V1PyTorchJobList # noqa: E501 +from pytorchjob.rest import ApiException + + +class TestV1PyTorchJobList(unittest.TestCase): + """V1PyTorchJobList unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testV1PyTorchJobList(self): + """Test V1PyTorchJobList""" + # FIXME: construct object with mandatory attributes with example values + # model = pytorchjob.models.v1_py_torch_job_list.V1PyTorchJobList() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/sdk/python/test/test_v1_py_torch_job_spec.py b/sdk/python/test/test_v1_py_torch_job_spec.py new file mode 100644 index 000000000..5d3916b5c --- /dev/null +++ b/sdk/python/test/test_v1_py_torch_job_spec.py @@ -0,0 +1,54 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +import unittest + +import pytorchjob +from pytorchjob.models.v1_py_torch_job_spec import V1PyTorchJobSpec # noqa: E501 +from pytorchjob.rest import ApiException + + +class TestV1PyTorchJobSpec(unittest.TestCase): + """V1PyTorchJobSpec unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testV1PyTorchJobSpec(self): + """Test V1PyTorchJobSpec""" + # FIXME: construct object with mandatory attributes with example values + # model = pytorchjob.models.v1_py_torch_job_spec.V1PyTorchJobSpec() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/sdk/python/test/test_v1_replica_spec.py b/sdk/python/test/test_v1_replica_spec.py new file mode 100644 index 000000000..7bb61d7f8 --- /dev/null +++ b/sdk/python/test/test_v1_replica_spec.py @@ -0,0 +1,54 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +import unittest + +import pytorchjob +from pytorchjob.models.v1_replica_spec import V1ReplicaSpec # noqa: E501 +from pytorchjob.rest import ApiException + + +class TestV1ReplicaSpec(unittest.TestCase): + """V1ReplicaSpec unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testV1ReplicaSpec(self): + """Test V1ReplicaSpec""" + # FIXME: construct object with mandatory attributes with example values + # model = pytorchjob.models.v1_replica_spec.V1ReplicaSpec() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/sdk/python/test/test_v1_replica_status.py b/sdk/python/test/test_v1_replica_status.py new file mode 100644 index 000000000..3cb612d23 --- /dev/null +++ b/sdk/python/test/test_v1_replica_status.py @@ -0,0 +1,54 @@ +# Copyright 2019 kubeflow.org. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# coding: utf-8 + +""" + pytorch + + Python SDK for PyTorch-Operator # noqa: E501 + + OpenAPI spec version: v0.1 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + + +from __future__ import absolute_import + +import unittest + +import pytorchjob +from pytorchjob.models.v1_replica_status import V1ReplicaStatus # noqa: E501 +from pytorchjob.rest import ApiException + + +class TestV1ReplicaStatus(unittest.TestCase): + """V1ReplicaStatus unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testV1ReplicaStatus(self): + """Test V1ReplicaStatus""" + # FIXME: construct object with mandatory attributes with example values + # model = pytorchjob.models.v1_replica_status.V1ReplicaStatus() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main()